Skip to content
This repository has been archived by the owner on Feb 15, 2023. It is now read-only.

Commit

Permalink
Improve client and server event.
Browse files Browse the repository at this point in the history
  • Loading branch information
uhm0311 committed May 19, 2020
1 parent 1013e13 commit fe50a8b
Show file tree
Hide file tree
Showing 17 changed files with 478 additions and 294 deletions.
4 changes: 2 additions & 2 deletions SocketIOSharp-Example Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ static void Main(string[] args)

static void InitEventHandlers(SocketIOClient client)
{
client.On(SocketIOEvent.CONNECTION, (Data) =>
client.On(SocketIOEvent.CONNECTION, () =>
{
Console.WriteLine("Connected!");
});

client.On(SocketIOEvent.DISCONNECT, (Data) =>
client.On(SocketIOEvent.DISCONNECT, () =>
{
Console.WriteLine();
Console.WriteLine("Disconnected!");
Expand Down
2 changes: 1 addition & 1 deletion SocketIOSharp-Example Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static void Main(string[] args)
socket.Emit("echo", data);
});

socket.On(SocketIOEvent.DISCONNECT, (data) =>
socket.On(SocketIOEvent.DISCONNECT, () =>
{
Console.WriteLine("Client disconnected!");
});
Expand Down
20 changes: 20 additions & 0 deletions SocketIOSharp/Client/SocketIOClient.Event.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace SocketIOSharp.Client
{
partial class SocketIOClient
{
private static class Event
{
public static readonly string CONNECT_ERROR = "connect_error";

public static readonly string RECONNECT = "reconnect";
public static readonly string RECONNECT_ATTEMPT = "reconnect_attempt";
public static readonly string RECONNECTING = "reconnecting";

public static readonly string RECONNECT_ERROR = "reconnect_error";
public static readonly string RECONNECT_FAILED = "reconnect_failed";

public static readonly string PING = "ping";
public static readonly string PONG = "pong";
}
}
}
12 changes: 8 additions & 4 deletions SocketIOSharp/Client/SocketIOClient.Send.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
{
partial class SocketIOClient
{
protected override void Emit(string Data)
protected override SocketIOClient Emit(string Data)
{
Client.Send(Data);
Client?.Send(Data);

return this;
}

protected override void Emit(byte[] RawData)
protected override SocketIOClient Emit(byte[] RawData)
{
Client.Send(RawData);
Client?.Send(RawData);

return this;
}
}
}
122 changes: 94 additions & 28 deletions SocketIOSharp/Client/SocketIOClient.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,120 @@
using EngineIOSharp.Client;
using EngineIOSharp.Common.Enum;
using EngineIOSharp.Common.Packet;
using SocketIOSharp.Common;
using SocketIOSharp.Common.Abstract.Connection;
using System;
using System.Threading;

namespace SocketIOSharp.Client
{
public partial class SocketIOClient : SocketIOConnection
public partial class SocketIOClient : SocketIOConnection<SocketIOClient>
{
private readonly EngineIOClient Client;
private static readonly Random Random = new Random();
private DateTime LastPing = DateTime.UtcNow;

private EngineIOClient Client = null;
private ulong ReconnectionAttempts = 0;

public SocketIOClientOption Option { get; private set; }
public bool AutoReconnect { get; set; }

public override EngineIOReadyState ReadyState => Client.ReadyState;
public override EngineIOReadyState ReadyState => Client?.ReadyState ?? EngineIOReadyState.CLOSED;

public SocketIOClient(SocketIOClientOption Option)
{
AutoReconnect = Option.AutoReconnect;
UseAckTimeout = Option.UseAckTimeout;

Client = new EngineIOClient(this.Option = Option);
Client.OnOpen(() =>
{
AckManager.SetTimeout(Client.Handshake.PingTimeout);
Closed = false;
});
this.Option = Option;
}

Client.OnMessage(OnPacket);
Client.OnClose(() =>
public SocketIOClient Connect()
{
if (Client == null)
{
OnDisconnect();
ReconnectionAttempts = 0;

if (AutoReconnect)
void Connect()
{
Connect();
Client = new EngineIOClient(Option);
Client.OnOpen(() =>
{
AckManager.SetTimeout(Client.Handshake.PingTimeout);
AckManager.StartTimer();

if (ReconnectionAttempts > 0)
{
CallEventHandler(Event.RECONNECT, ReconnectionAttempts);
ReconnectionAttempts = 0;
}
});

Client.OnMessage(OnPacket);
Client.OnClose((Exception) =>
{
OnDisconnect(Exception);

if (ReconnectionAttempts == 0)
{
CallEventHandler(Event.CONNECT_ERROR, new SocketIOException("Connect error", Exception).ToString());
}
else
{
CallEventHandler(Event.RECONNECT_ERROR, new SocketIOException("Reconnect error", Exception).ToString());
}

if (Option.Reconnection && ReconnectionAttempts < Option.ReconnectionAttempts)
{
ReconnectionAttempts++;

ThreadPool.QueueUserWorkItem((_) =>
{
int Factor = (int)(Option.ReconnectionDelay * Option.RandomizationFactor);
int Delay = Random.Next(Option.ReconnectionDelay - Factor, Option.ReconnectionDelay + Factor + 1);

Thread.Sleep(Delay);
Option.ReconnectionDelay = Math.Min(Option.ReconnectionDelayMax, Option.ReconnectionDelay * 2);

CallEventHandler(Event.RECONNECT_ATTEMPT);
Connect();

CallEventHandler(Event.RECONNECTING, ReconnectionAttempts);
});
}
else if (ReconnectionAttempts >= Option.ReconnectionAttempts)
{
CallEventHandler(Event.RECONNECT_FAILED);
}
});

Client.On(EngineIOClient.Event.PACKET_CREATE, (Argument) =>
{
if ((Argument as EngineIOPacket).Type == EngineIOPacketType.PING)
{
CallEventHandler(Event.PING);
LastPing = DateTime.UtcNow;
}
});

Client.On(EngineIOClient.Event.PACKET, (Argument) =>
{
if ((Argument as EngineIOPacket).Type == EngineIOPacketType.PONG)
{
CallEventHandler(Event.PONG, DateTime.UtcNow.Subtract(LastPing).TotalMilliseconds);
}
});

Client.Connect();
}
});
}

public void Connect()
{
Client.Connect();
Connect();
}

return this;
}

public override void Close()
public override SocketIOClient Close()
{
AutoReconnect = false;
Client?.Close();

Client.Close();
AckManager.Dispose();
Reconstructor.Dispose();
return this;
}
}
}
63 changes: 55 additions & 8 deletions SocketIOSharp/Client/SocketIOClientOption.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using EngineIOSharp.Client;
using EngineIOSharp.Common.Enum;
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
Expand All @@ -8,8 +9,46 @@ namespace SocketIOSharp.Client
{
public class SocketIOClientOption : EngineIOClientOption
{
public bool UseAckTimeout { get; private set; }
public bool AutoReconnect { get; private set; }
private int _ReconnectionDelay;
private int _ReconnectionDelayMax;
private double _RandomizationFactor;

public bool Reconnection { get; set; }
public ulong ReconnectionAttempts { get; set; }

public int ReconnectionDelay
{
get
{
return _ReconnectionDelay;
}
set
{
_ReconnectionDelay = Math.Max(0, value);
}
}
public int ReconnectionDelayMax
{
get
{
return _ReconnectionDelayMax;
}
set
{
_ReconnectionDelayMax = Math.Max(ReconnectionDelay, value);
}
}
public double RandomizationFactor
{
get
{
return _RandomizationFactor;
}
set
{
_RandomizationFactor = Math.Max(0, Math.Min(1, value));
}
}

/// <summary>
/// Options for Socket.IO client.
Expand All @@ -18,8 +57,12 @@ public class SocketIOClientOption : EngineIOClientOption
/// <param name="Host">Host to connect to.</param>
/// <param name="Port">Port to connect to.</param>
/// <param name="PolicyPort">Port the policy server listens on.</param>
/// <param name="UseAckTimeout">Whether to use ack timeout or not.</param>
/// <param name="AutoReconnect">Whether to reconnect to server after Engine.IO client is closed or not.</param>
/// <param name="Path">Path to connect to.</param>
/// <param name="Reconnection">Whether to reconnect to server after Engine.IO client is closed or not.</param>
/// <param name="ReconnectionAttempts">Number of reconnection attempts before giving up.</param>
/// <param name="ReconnectionDelay">How ms to initially wait before attempting a new reconnection.</param>
/// <param name="ReconnectionDelayMax">Maximum amount of time to wait between reconnections. Each attempt increases the <see cref="ReconnectionDelay"/> by 2x along with a randomization.</param>
/// <param name="RandomizationFactor">0 &lt;= <see cref="RandomizationFactor"/> &lt;= 1.</param>
/// <param name="Query">Parameters that will be passed for each request to the server.</param>
/// <param name="Upgrade">Whether the client should try to upgrade the transport.</param>
/// <param name="RemeberUpgrade">Whether the client should bypass normal upgrade process when previous websocket connection is succeeded.</param>
Expand All @@ -35,17 +78,21 @@ public class SocketIOClientOption : EngineIOClientOption
/// <param name="ClientCertificates">The collection of security certificates that are associated with each request.</param>
/// <param name="ClientCertificateSelectionCallback">Callback used to select the certificate to supply to the server.</param>
/// <param name="ServerCertificateValidationCallback">Callback method to validate the server certificate.</param>
public SocketIOClientOption(EngineIOScheme Scheme, string Host, ushort Port, ushort PolicyPort = 843, bool UseAckTimeout = false, bool AutoReconnect = true, IDictionary<string, string> Query = null, bool Upgrade = true, bool RemeberUpgrade = false, bool ForceBase64 = false, bool WithCredentials = true, bool? TimestampRequests = null, string TimestampParam = "t", bool Polling = true, int PollingTimeout = 0, bool WebSocket = true, string[] WebSocketSubprotocols = null, IDictionary<string, string> ExtraHeaders = null, X509CertificateCollection ClientCertificates = null, LocalCertificateSelectionCallback ClientCertificateSelectionCallback = null, RemoteCertificateValidationCallback ServerCertificateValidationCallback = null) : base(Scheme, Host, Port, PolicyPort, "/socket.io", PolishQuery(Query), Upgrade, RemeberUpgrade, ForceBase64, WithCredentials, TimestampRequests, TimestampParam, Polling, PollingTimeout, WebSocket, WebSocketSubprotocols, ExtraHeaders, ClientCertificates, ClientCertificateSelectionCallback, ServerCertificateValidationCallback)
public SocketIOClientOption(EngineIOScheme Scheme, string Host, ushort Port, ushort PolicyPort = 843, string Path = "/socket.io", bool Reconnection = true, ulong ReconnectionAttempts = ulong.MaxValue, int ReconnectionDelay = 1000, int ReconnectionDelayMax = 5000, double RandomizationFactor = 0.5, IDictionary<string, string> Query = null, bool Upgrade = true, bool RemeberUpgrade = false, bool ForceBase64 = false, bool WithCredentials = true, bool? TimestampRequests = null, string TimestampParam = "t", bool Polling = true, int PollingTimeout = 0, bool WebSocket = true, string[] WebSocketSubprotocols = null, IDictionary<string, string> ExtraHeaders = null, X509CertificateCollection ClientCertificates = null, LocalCertificateSelectionCallback ClientCertificateSelectionCallback = null, RemoteCertificateValidationCallback ServerCertificateValidationCallback = null) : base(Scheme, Host, Port, PolicyPort, Path, PolishQuery(Query), Upgrade, RemeberUpgrade, ForceBase64, WithCredentials, TimestampRequests, TimestampParam, Polling, PollingTimeout, WebSocket, WebSocketSubprotocols, ExtraHeaders, ClientCertificates, ClientCertificateSelectionCallback, ServerCertificateValidationCallback)
{
this.UseAckTimeout = UseAckTimeout;
this.AutoReconnect = AutoReconnect;
this.Reconnection = Reconnection;
this.ReconnectionAttempts = ReconnectionAttempts;

this.ReconnectionDelay = ReconnectionDelay;
this.ReconnectionDelayMax = ReconnectionDelayMax;
this.RandomizationFactor = RandomizationFactor;
}

private static IDictionary<string, string> PolishQuery(IDictionary<string, string> Query)
{
Query = new Dictionary<string, string>(Query ?? new Dictionary<string, string>());

if (!Query.ContainsKey("EIO") || Query["EIO"].Equals("3"))
if (!Query.ContainsKey("EIO"))
{
Query["EIO"] = "4";
}
Expand Down
Loading

0 comments on commit fe50a8b

Please sign in to comment.