Added NightAlert project for travel kit

This commit is contained in:
2021-06-10 14:39:06 -04:00
commit d38d9e3b7e
308 changed files with 35922 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
using System;
namespace Quobject.SocketIoClientDotNet.Client
{
public class AckImpl : IAck
{
private readonly Action fn0;
private readonly Action<object> fn1;
private readonly Action<object, object> fn2;
private readonly Action<object, object, object> fn3;
private readonly Action<object, object, object, object> fn4;
public AckImpl(Action fn)
{
fn0 = fn;
}
public AckImpl(Action<object> fn)
{
fn1 = fn;
}
public AckImpl(Action<object, object> fn)
{
fn2 = fn;
}
public AckImpl(Action<object, object, object> fn)
{
fn3 = fn;
}
public AckImpl(Action<object, object, object, object> fn)
{
fn4 = fn;
}
public void Call(params object[] args)
{
if (fn0 != null)
{
fn0();
}
else if (fn1 != null)
{
var arg0 = args.Length > 0 ? args[0] : null;
fn1(arg0);
}
else if (fn2 != null)
{
var arg0 = args.Length > 0 ? args[0] : null;
var arg1 = args.Length > 1 ? args[1] : null;
fn2(arg0, arg1);
}
else if (fn3 != null)
{
var arg0 = args.Length > 0 ? args[0] : null;
var arg1 = args.Length > 1 ? args[1] : null;
var arg2 = args.Length > 2 ? args[2] : null;
fn3(arg0, arg1, arg2);
}
else if (fn4 != null)
{
var arg0 = args.Length > 0 ? args[0] : null;
var arg1 = args.Length > 1 ? args[1] : null;
var arg2 = args.Length > 2 ? args[2] : null;
var arg3 = args.Length > 3 ? args[3] : null;
fn4(arg0, arg1, arg2, arg3);
}
}
}
}

View File

@@ -0,0 +1,8 @@

namespace Quobject.SocketIoClientDotNet.Client
{
public interface IAck
{
void Call(params object[] args);
}
}

View File

@@ -0,0 +1,74 @@
using System.Collections.Immutable;
using Quobject.EngineIoClientDotNet.Modules;
using System;
namespace Quobject.SocketIoClientDotNet.Client
{
public class IO
{
private static readonly ImmutableDictionary<string, Manager> Managers = ImmutableDictionary.Create<string, Manager>();
/// <summary>
/// Protocol version
/// </summary>
public static int Protocol = Parser.Parser.protocol;
private IO()
{
}
public static Socket Socket(string uri)
{
return Socket(uri, null);
}
public static Socket Socket(string uri, Options opts)
{
return Socket(Url.Parse(uri), opts);
}
public static Socket Socket(Uri uri)
{
return Socket(uri, null);
}
public static Socket Socket(Uri uri, Options opts)
{
var log = LogManager.GetLogger(Global.CallerName());
if (opts == null)
{
opts = new Options();
}
Manager io;
if (opts.ForceNew || !opts.Multiplex)
{
log.Info(string.Format("ignoring socket cache for {0}", uri.ToString()));
io = new Manager(uri, opts);
}
else
{
var id = Url.ExtractId(uri);
if (!Managers.ContainsKey(id))
{
log.Info( string.Format("new io instance for {0}", id));
Managers.Add(id, new Manager(uri, opts));
}
io = Managers[id];
}
return io.Socket(uri.PathAndQuery);
}
public class Options : Client.Options
{
public bool ForceNew = true;
public bool Multiplex = true;
}
}
}

View File

@@ -0,0 +1,76 @@

using System.Collections.Concurrent;
using Quobject.EngineIoClientDotNet.Modules;
using System;
namespace Quobject.SocketIoClientDotNet.Client
{
public class IO
{
private static readonly ConcurrentDictionary<string, Manager> Managers = new ConcurrentDictionary<string, Manager>();
/// <summary>
/// Protocol version
/// </summary>
public static int Protocol = Parser.Parser.protocol;
private IO()
{
}
public static Socket Socket(string uri)
{
return Socket(uri, null);
}
public static Socket Socket(string uri, Options opts)
{
return Socket(Url.Parse(uri), opts);
}
public static Socket Socket(Uri uri)
{
return Socket(uri, null);
}
public static Socket Socket(Uri uri, Options opts)
{
var log = LogManager.GetLogger(Global.CallerName());
if (opts == null)
{
opts = new Options();
}
Manager io;
if (opts.ForceNew || !opts.Multiplex)
{
log.Info(string.Format("ignoring socket cache for {0}", uri.ToString()));
io = new Manager(uri, opts);
}
else
{
var id = Url.ExtractId(uri);
if (!Managers.ContainsKey(id))
{
log.Info( string.Format("new io instance for {0}", id));
Managers.TryAdd(id, new Manager(uri, opts));
}
io = Managers[id];
}
return io.Socket(uri.PathAndQuery);
}
public class Options : Client.Options
{
public bool ForceNew = true;
public bool Multiplex = true;
}
}
}

View File

@@ -0,0 +1,550 @@
using System.Collections.Immutable;
using Quobject.EngineIoClientDotNet.ComponentEmitter;
using Quobject.EngineIoClientDotNet.Modules;
using Quobject.EngineIoClientDotNet.Thread;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
namespace Quobject.SocketIoClientDotNet.Client
{
public class Manager : Emitter
{
public enum ReadyStateEnum
{
OPENING,
OPEN,
CLOSED
}
public static readonly string EVENT_OPEN = "open";
public static readonly string EVENT_CLOSE = "close";
public static readonly string EVENT_PACKET = "packet";
public static readonly string EVENT_ERROR = "error";
public static readonly string EVENT_CONNECT_ERROR = "connect_error";
public static readonly string EVENT_CONNECT_TIMEOUT = "connect_timeout";
public static readonly string EVENT_RECONNECT = "reconnect";
public static readonly string EVENT_RECONNECT_ERROR = "reconnect_error";
public static readonly string EVENT_RECONNECT_FAILED = "reconnect_failed";
public static readonly string EVENT_RECONNECT_ATTEMPT = "reconnect_attempt";
public static readonly string EVENT_RECONNECTING = "reconnecting";
/*package*/
public ReadyStateEnum ReadyState = ReadyStateEnum.CLOSED;
private bool _reconnection;
private bool SkipReconnect;
private bool Reconnecting;
private bool Encoding;
private bool OpenReconnect;
private int _reconnectionAttempts;
private long _reconnectionDelay;
private long _reconnectionDelayMax;
private long _timeout;
private int Attempts;
private Uri Uri;
private List<Parser.Packet> PacketBuffer;
private ConcurrentQueue<On.IHandle> Subs;
private Quobject.EngineIoClientDotNet.Client.Socket.Options Opts;
private bool AutoConnect;
private HashSet<Socket> OpeningSockets;
/*package*/
public Quobject.EngineIoClientDotNet.Client.Socket EngineSocket;
private Parser.Parser.Encoder Encoder;
private Parser.Parser.Decoder Decoder;
/**
* This ImmutableDictionary can be accessed from outside of EventThread.
*/
private ImmutableDictionary<string, Socket> Nsps;
public Manager() : this(null, null)
{
}
public Manager(Uri uri) : this(uri, null)
{
}
public Manager(Options opts) : this(null, opts)
{
}
public Manager(Uri uri, Options opts)
{
if (opts == null)
{
opts = new Options();
}
if (opts.Path == null)
{
opts.Path = "/socket.io";
}
this.Opts = opts;
this.Nsps = ImmutableDictionary.Create<string, Socket>();
this.Subs = new ConcurrentQueue<On.IHandle>();
this.Reconnection(opts.Reconnection);
this.ReconnectionAttempts(opts.ReconnectionAttempts != 0 ? opts.ReconnectionAttempts : int.MaxValue);
this.ReconnectionDelay(opts.ReconnectionDelay != 0 ? opts.ReconnectionDelay : 1000);
this.ReconnectionDelayMax(opts.ReconnectionDelayMax != 0 ? opts.ReconnectionDelayMax : 5000);
this.Timeout(opts.Timeout < 0 ? 20000 : opts.Timeout);
this.ReadyState = ReadyStateEnum.CLOSED;
this.Uri = uri;
this.Attempts = 0;
this.Encoding = false;
this.PacketBuffer = new List<Parser.Packet>();
this.OpeningSockets = new HashSet<Socket>();
this.Encoder = new Parser.Parser.Encoder();
this.Decoder = new Parser.Parser.Decoder();
this.AutoConnect = opts.AutoConnect;
if (AutoConnect)
{
Open();
}
}
private void EmitAll(string eventString, params object[] args)
{
Emit(eventString, args);
foreach (var socket in Nsps.Values)
{
socket.Emit(eventString, args);
}
}
public bool Reconnection()
{
return _reconnection;
}
private Manager Reconnection(bool v)
{
_reconnection = v;
return this;
}
public int ReconnectionAttempts()
{
return _reconnectionAttempts;
}
private Manager ReconnectionAttempts(int v)
{
_reconnectionAttempts = v;
return this;
}
public long ReconnectionDelay()
{
return _reconnectionDelay;
}
private Manager ReconnectionDelay(long v)
{
_reconnectionDelay = v;
return this;
}
public long ReconnectionDelayMax()
{
return _reconnectionDelayMax;
}
private Manager ReconnectionDelayMax(long v)
{
_reconnectionDelayMax = v;
return this;
}
public long Timeout()
{
return _timeout;
}
private Manager Timeout(long v)
{
_timeout = v;
return this;
}
private void MaybeReconnectOnOpen()
{
if (!this.OpenReconnect && !this.Reconnecting && this._reconnection)
{
this.OpenReconnect = true;
this.Reconnect();
}
}
public Manager Open()
{
return Open(null);
}
private Manager Open(IOpenCallback fn)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("readyState {0}", ReadyState));
if (ReadyState == ReadyStateEnum.OPEN)
{
return this;
}
log.Info(string.Format("opening {0}", Uri));
EngineSocket = new Engine(Uri, Opts);
Quobject.EngineIoClientDotNet.Client.Socket socket = EngineSocket;
ReadyState = ReadyStateEnum.OPENING;
OpeningSockets.Add(Socket(Uri.PathAndQuery));
SkipReconnect = false;
var openSub = SocketIoClientDotNet.Client.On.Create(socket, Engine.EVENT_OPEN, new ListenerImpl(() =>
{
OnOpen();
if (fn != null)
{
fn.Call(null);
}
}));
var errorSub = Client.On.Create(socket, Engine.EVENT_ERROR, new ListenerImpl((data) =>
{
log.Info("connect_error");
Cleanup();
ReadyState = ReadyStateEnum.CLOSED;
EmitAll(EVENT_CONNECT_ERROR, data);
if (fn != null)
{
var err = new SocketIOException("Connection error", data is Exception ? (Exception) data : null);
fn.Call(err);
}
MaybeReconnectOnOpen();
}));
if (_timeout >= 0)
{
var timeout = (int) _timeout;
log.Info(string.Format("connection attempt will timeout after {0}", timeout));
var timer = EasyTimer.SetTimeout(() =>
{
var log2 = LogManager.GetLogger(Global.CallerName());
log2.Info("Manager Open start");
log2.Info(string.Format("connect attempt timed out after {0}", timeout));
openSub.Destroy();
socket.Close();
socket.Emit(Engine.EVENT_ERROR, new SocketIOException("timeout"));
EmitAll(EVENT_CONNECT_TIMEOUT, timeout);
log2.Info("Manager Open finish");
}, timeout);
Subs.Enqueue(new On.ActionHandleImpl(timer.Stop));
;
}
Subs.Enqueue(openSub);
Subs.Enqueue(errorSub);
EngineSocket.Open();
return this;
}
private void OnOpen()
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info("open");
Cleanup();
ReadyState = ReadyStateEnum.OPEN;
Emit(EVENT_OPEN);
var socket = EngineSocket;
var sub = Client.On.Create(socket, Engine.EVENT_DATA, new ListenerImpl((data) =>
{
if (data is string)
{
OnData((string)data);
}
else if (data is byte[])
{
Ondata((byte[])data);
}
}));
Subs.Enqueue(sub);
sub = Client.On.Create(this.Decoder, Parser.Parser.Decoder.EVENT_DECODED, new ListenerImpl((data) =>
{
OnDecoded((Parser.Packet)data);
}));
Subs.Enqueue(sub);
sub = Client.On.Create(socket, Engine.EVENT_ERROR, new ListenerImpl((data) =>
{
OnError((Exception) data);
}));
Subs.Enqueue(sub);
sub = Client.On.Create(socket, Engine.EVENT_CLOSE, new ListenerImpl((data) =>
{
OnClose((string) data);
}));
Subs.Enqueue(sub);
}
private void OnData(string data)
{
this.Decoder.Add(data);
}
private void Ondata(byte[] data)
{
this.Decoder.Add(data);
}
private void OnDecoded(Parser.Packet packet)
{
this.Emit(EVENT_PACKET, packet);
}
private void OnError(Exception err)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Error("error", err);
this.EmitAll(EVENT_ERROR, err);
}
public Socket Socket(string nsp)
{
if (Nsps.ContainsKey(nsp))
{
return Nsps[nsp];
}
var socket = new Socket(this,nsp);
Nsps = Nsps.Add(nsp, socket);
return socket;
}
internal void Destroy(Socket socket)
{
OpeningSockets.Remove(socket);
if (OpeningSockets.Count == 0)
{
Close();
}
}
internal void Packet(Parser.Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("writing packet {0}", packet));
if (!Encoding)
{
Encoding = true;
Encoder.Encode(packet, new Parser.Parser.Encoder.CallbackImp((data) =>
{
var encodedPackets = (object[]) data;
foreach (var packet1 in encodedPackets)
{
if (packet1 is string)
{
EngineSocket.Write((string) packet1);
}
else if (packet1 is byte[])
{
EngineSocket.Write((byte[]) packet1);
}
}
Encoding = false;
ProcessPacketQueue();
}));
}
else
{
PacketBuffer.Add(packet);
}
}
private void ProcessPacketQueue()
{
if (this.PacketBuffer.Count > 0 && !this.Encoding)
{
Parser.Packet pack = this.PacketBuffer[0];
PacketBuffer.Remove(pack);
this.Packet(pack);
}
}
private void Cleanup()
{
// dequeue and destroy until empty
while (Subs.TryDequeue(out On.IHandle sub))
{
sub.Destroy();
}
}
public void Close()
{
this.SkipReconnect = true;
this.Reconnecting = false;
if (ReadyState != ReadyStateEnum.OPEN)
{
Cleanup();
}
ReadyState = ReadyStateEnum.CLOSED;
if (EngineSocket != null)
{
this.EngineSocket.Close();
}
}
private void OnClose(string reason)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info("start");
Cleanup();
ReadyState = ReadyStateEnum.CLOSED;
Emit(EVENT_CLOSE, reason);
if (_reconnection && !SkipReconnect)
{
Reconnect();
}
}
private void Reconnect()
{
var log = LogManager.GetLogger(Global.CallerName());
if (Reconnecting || SkipReconnect)
{
return;
}
Attempts++;
if (Attempts > _reconnectionAttempts)
{
log.Info("reconnect failed");
EmitAll(EVENT_RECONNECT_FAILED);
Reconnecting = false;
}
else
{
var delay = Attempts*ReconnectionDelay();
delay = Math.Min(delay, ReconnectionDelayMax());
log.Info(string.Format("will wait {0}ms before reconnect attempt", delay));
Reconnecting = true;
var timer = EasyTimer.SetTimeout(() =>
{
var log2 = LogManager.GetLogger(Global.CallerName());
log2.Info("EasyTimer Reconnect start");
log2.Info(string.Format("attempting reconnect"));
EmitAll(EVENT_RECONNECT_ATTEMPT, Attempts);
EmitAll(EVENT_RECONNECTING, Attempts);
Open(new OpenCallbackImp((err) =>
{
if (err != null)
{
log.Error("reconnect attempt error", (Exception) err);
Reconnecting = false;
Reconnect();
EmitAll(EVENT_RECONNECT_ERROR, (Exception) err);
}
else
{
log.Info("reconnect success");
OnReconnect();
}
}));
log2.Info("EasyTimer Reconnect finish");
}, (int)delay);
Subs.Enqueue(new On.ActionHandleImpl(timer.Stop));
}
}
private void OnReconnect()
{
int attempts = this.Attempts;
this.Attempts = 0;
this.Reconnecting = false;
this.EmitAll(EVENT_RECONNECT, attempts);
}
public interface IOpenCallback
{
void Call(Exception err);
}
public class OpenCallbackImp : IOpenCallback
{
private Action<object> Fn;
public OpenCallbackImp(Action<object> fn)
{
Fn = fn;
}
public void Call(Exception err)
{
Fn(err);
}
}
}
public class Engine : Quobject.EngineIoClientDotNet.Client.Socket
{
public Engine(Uri uri, Options opts) : base(uri, opts)
{
}
}
public class Options : Quobject.EngineIoClientDotNet.Client.Socket.Options
{
public bool Reconnection = true;
public int ReconnectionAttempts;
public long ReconnectionDelay;
public long ReconnectionDelayMax;
public long Timeout = -1;
public bool AutoConnect = true;
}
}

View File

@@ -0,0 +1,562 @@

using Quobject.EngineIoClientDotNet.ComponentEmitter;
using Quobject.EngineIoClientDotNet.Modules;
using Quobject.EngineIoClientDotNet.Thread;
using System;
using System.Collections.Generic;
using System.Text;
namespace Quobject.SocketIoClientDotNet.Client
{
public class Manager : Emitter
{
public enum ReadyStateEnum
{
OPENING,
OPEN,
CLOSED
}
public static readonly string EVENT_OPEN = "open";
public static readonly string EVENT_CLOSE = "close";
public static readonly string EVENT_PACKET = "packet";
public static readonly string EVENT_ERROR = "error";
public static readonly string EVENT_CONNECT_ERROR = "connect_error";
public static readonly string EVENT_CONNECT_TIMEOUT = "connect_timeout";
public static readonly string EVENT_RECONNECT = "reconnect";
public static readonly string EVENT_RECONNECT_ERROR = "reconnect_error";
public static readonly string EVENT_RECONNECT_FAILED = "reconnect_failed";
public static readonly string EVENT_RECONNECT_ATTEMPT = "reconnect_attempt";
public static readonly string EVENT_RECONNECTING = "reconnecting";
/*package*/
public ReadyStateEnum ReadyState = ReadyStateEnum.CLOSED;
private bool _reconnection;
private bool SkipReconnect;
private bool Reconnecting;
private bool Encoding;
private bool OpenReconnect;
private int _reconnectionAttempts;
private long _reconnectionDelay;
private long _reconnectionDelayMax;
private long _timeout;
private int Connected;
private int Attempts;
private Uri Uri;
private List<Parser.Packet> PacketBuffer;
private Queue<On.IHandle> Subs;
private Quobject.EngineIoClientDotNet.Client.Socket.Options Opts;
private bool AutoConnect;
/*package*/
public Quobject.EngineIoClientDotNet.Client.Socket EngineSocket;
private Parser.Parser.Encoder Encoder;
private Parser.Parser.Decoder Decoder;
/**
* This ConcurrentDictionary can be accessed from outside of EventThread.
*/
private System.Collections.Concurrent.ConcurrentDictionary<string, Socket> Nsps;
public Manager() : this(null, null)
{
}
public Manager(Uri uri) : this(uri, null)
{
}
public Manager(Options opts) : this(null, opts)
{
}
public Manager(Uri uri, Options opts)
{
if (opts == null)
{
opts = new Options();
}
if (opts.Path == null)
{
opts.Path = "/socket.io";
}
this.Opts = opts;
this.Nsps = new System.Collections.Concurrent.ConcurrentDictionary<string, Socket>();
this.Subs = new Queue<On.IHandle>();
this.Reconnection(opts.Reconnection);
this.ReconnectionAttempts(opts.ReconnectionAttempts != 0 ? opts.ReconnectionAttempts : int.MaxValue);
this.ReconnectionDelay(opts.ReconnectionDelay != 0 ? opts.ReconnectionDelay : 1000);
this.ReconnectionDelayMax(opts.ReconnectionDelayMax != 0 ? opts.ReconnectionDelayMax : 5000);
this.Timeout(opts.Timeout < 0 ? 20000 : opts.Timeout);
this.ReadyState = ReadyStateEnum.CLOSED;
this.Uri = uri;
this.Connected = 0;
this.Attempts = 0;
this.Encoding = false;
this.PacketBuffer = new List<Parser.Packet>();
this.Encoder = new Parser.Parser.Encoder();
this.Decoder = new Parser.Parser.Decoder();
this.AutoConnect = opts.AutoConnect;
if (AutoConnect)
{
Open();
}
}
private void EmitAll(string eventString, params object[] args)
{
Emit(eventString, args);
foreach (var socket in Nsps.Values)
{
socket.Emit(eventString, args);
}
}
public bool Reconnection()
{
return _reconnection;
}
private Manager Reconnection(bool v)
{
_reconnection = v;
return this;
}
public int ReconnectionAttempts()
{
return _reconnectionAttempts;
}
private Manager ReconnectionAttempts(int v)
{
_reconnectionAttempts = v;
return this;
}
public long ReconnectionDelay()
{
return _reconnectionDelay;
}
private Manager ReconnectionDelay(long v)
{
_reconnectionDelay = v;
return this;
}
public long ReconnectionDelayMax()
{
return _reconnectionDelayMax;
}
private Manager ReconnectionDelayMax(long v)
{
_reconnectionDelayMax = v;
return this;
}
public long Timeout()
{
return _timeout;
}
private Manager Timeout(long v)
{
_timeout = v;
return this;
}
private void MaybeReconnectOnOpen()
{
if (!this.OpenReconnect && !this.Reconnecting && this._reconnection)
{
this.OpenReconnect = true;
this.Reconnect();
}
}
public Manager Open()
{
return Open(null);
}
private Manager Open(IOpenCallback fn)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("readyState {0}", ReadyState));
if (ReadyState == ReadyStateEnum.OPEN)
{
return this;
}
log.Info(string.Format("opening {0}", Uri));
EngineSocket = new Engine(Uri, Opts);
Quobject.EngineIoClientDotNet.Client.Socket socket = EngineSocket;
ReadyState = ReadyStateEnum.OPENING;
var openSub = SocketIoClientDotNet.Client.On.Create(socket, Engine.EVENT_OPEN, new ListenerImpl(() =>
{
OnOpen();
if (fn != null)
{
fn.Call(null);
}
}));
var errorSub = Client.On.Create(socket, Engine.EVENT_ERROR, new ListenerImpl((data) =>
{
log.Info("connect_error");
Cleanup();
ReadyState = ReadyStateEnum.CLOSED;
EmitAll(EVENT_CONNECT_ERROR, data);
if (fn != null)
{
var err = new SocketIOException("Connection error", data is Exception ? (Exception) data : null);
fn.Call(err);
}
MaybeReconnectOnOpen();
}));
if (_timeout >= 0)
{
var timeout = (int) _timeout;
log.Info(string.Format("connection attempt will timeout after {0}", timeout));
var timer = EasyTimer.SetTimeout(() =>
{
var log2 = LogManager.GetLogger(Global.CallerName());
log2.Info("Manager Open start");
log2.Info(string.Format("connect attempt timed out after {0}", timeout));
openSub.Destroy();
socket.Close();
socket.Emit(Engine.EVENT_ERROR, new SocketIOException("timeout"));
EmitAll(EVENT_CONNECT_TIMEOUT, timeout);
log2.Info("Manager Open finish");
}, timeout);
lock (Subs)
{
Subs.Enqueue(new On.ActionHandleImpl(timer.Stop));
}
}
lock (Subs)
{
Subs.Enqueue(openSub);
Subs.Enqueue(errorSub);
}
EngineSocket.Open();
return this;
}
private void OnOpen()
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info("open");
Cleanup();
ReadyState = ReadyStateEnum.OPEN;
Emit(EVENT_OPEN);
var socket = EngineSocket;
var sub = Client.On.Create(socket, Engine.EVENT_DATA, new ListenerImpl((data) =>
{
if (data is string)
{
OnData((string)data);
}
else if (data is byte[])
{
Ondata((byte[])data);
}
}));
lock (Subs)
{
Subs.Enqueue(sub);
}
sub = Client.On.Create(this.Decoder, Parser.Parser.Decoder.EVENT_DECODED, new ListenerImpl((data) =>
{
OnDecoded((Parser.Packet)data);
}));
lock (Subs)
{
Subs.Enqueue(sub);
}
sub = Client.On.Create(socket, Engine.EVENT_ERROR, new ListenerImpl((data) =>
{
OnError((Exception) data);
}));
lock (Subs)
{
Subs.Enqueue(sub);
}
sub = Client.On.Create(socket, Engine.EVENT_CLOSE, new ListenerImpl((data) =>
{
OnClose((string) data);
}));
lock (Subs)
{
Subs.Enqueue(sub);
}
}
private void OnData(string data)
{
this.Decoder.Add(data);
}
private void Ondata(byte[] data)
{
this.Decoder.Add(data);
}
private void OnDecoded(Parser.Packet packet)
{
this.Emit(EVENT_PACKET, packet);
}
private void OnError(Exception err)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Error("error", err);
this.EmitAll(EVENT_ERROR, err);
}
public Socket Socket(string nsp)
{
if (Nsps.ContainsKey(nsp))
{
return Nsps[nsp];
}
var socket = new Socket(this,nsp);
Nsps.TryAdd(nsp, socket);
socket.On(Client.Socket.EVENT_CONNECT, new ListenerImpl(() =>
{
Connected++;
}));
return socket;
}
internal void Destroy(Socket socket)
{
--Connected;
if (Connected == 0)
{
Close();
}
}
internal void Packet(Parser.Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("writing packet {0}", packet));
if (!Encoding)
{
Encoding = true;
Encoder.Encode(packet, new Parser.Parser.Encoder.CallbackImp((data) =>
{
var encodedPackets = (object[]) data;
foreach (var packet1 in encodedPackets)
{
if (packet1 is string)
{
EngineSocket.Write((string) packet1);
}
else if (packet1 is byte[])
{
EngineSocket.Write((byte[]) packet1);
}
}
Encoding = false;
ProcessPacketQueue();
}));
}
else
{
PacketBuffer.Add(packet);
}
}
private void ProcessPacketQueue()
{
if (this.PacketBuffer.Count > 0 && !this.Encoding)
{
Parser.Packet pack = this.PacketBuffer[0];
PacketBuffer.Remove(pack);
this.Packet(pack);
}
}
private void Cleanup()
{
lock (Subs)
{
foreach (var sub in Subs)
{
sub.Destroy();
}
Subs.Clear();
}
}
public void Close()
{
this.SkipReconnect = true;
this.EngineSocket.Close();
}
private void OnClose(string reason)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info("start");
Cleanup();
ReadyState = ReadyStateEnum.CLOSED;
Emit(EVENT_CLOSE, reason);
if (_reconnection && !SkipReconnect)
{
Reconnect();
}
}
private void Reconnect()
{
var log = LogManager.GetLogger(Global.CallerName());
if (Reconnecting)
{
return;
}
Attempts++;
if (Attempts > _reconnectionAttempts)
{
log.Info("reconnect failed");
EmitAll(EVENT_RECONNECT_FAILED);
Reconnecting = false;
}
else
{
var delay = Attempts*ReconnectionDelay();
delay = Math.Min(delay, ReconnectionDelayMax());
log.Info(string.Format("will wait {0}ms before reconnect attempt", delay));
Reconnecting = true;
var timer = EasyTimer.SetTimeout(() =>
{
var log2 = LogManager.GetLogger(Global.CallerName());
log2.Info("EasyTimer Reconnect start");
log2.Info(string.Format("attempting reconnect"));
EmitAll(EVENT_RECONNECT_ATTEMPT, Attempts);
EmitAll(EVENT_RECONNECTING, Attempts);
Open(new OpenCallbackImp((err) =>
{
if (err != null)
{
log.Error("reconnect attempt error", (Exception) err);
Reconnecting = false;
Reconnect();
EmitAll(EVENT_RECONNECT_ERROR, (Exception) err);
}
else
{
log.Info("reconnect success");
OnReconnect();
}
}));
log2.Info("EasyTimer Reconnect finish");
}, (int)delay);
lock (Subs){
Subs.Enqueue(new On.ActionHandleImpl(timer.Stop));
}
}
}
private void OnReconnect()
{
int attempts = this.Attempts;
this.Attempts = 0;
this.Reconnecting = false;
this.EmitAll(EVENT_RECONNECT, attempts);
}
public interface IOpenCallback
{
void Call(Exception err);
}
public class OpenCallbackImp : IOpenCallback
{
private Action<object> Fn;
public OpenCallbackImp(Action<object> fn)
{
Fn = fn;
}
public void Call(Exception err)
{
Fn(err);
}
}
}
public class Engine : Quobject.EngineIoClientDotNet.Client.Socket
{
public Engine(Uri uri, Options opts) : base(uri, opts)
{
}
}
public class Options : Quobject.EngineIoClientDotNet.Client.Socket.Options
{
public bool Reconnection = true;
public int ReconnectionAttempts;
public long ReconnectionDelay;
public long ReconnectionDelayMax;
public long Timeout = -1;
public bool AutoConnect = true;
public bool DisablePing = false;
}
}

View File

@@ -0,0 +1,57 @@
using Quobject.EngineIoClientDotNet.ComponentEmitter;
using System;
namespace Quobject.SocketIoClientDotNet.Client
{
public class On
{
private On() { }
public static IHandle Create(Emitter obj, string ev, IListener fn)
{
obj.On(ev, fn);
return new HandleImpl(obj,ev,fn);
}
public class HandleImpl : IHandle
{
private Emitter obj;
private string ev;
private IListener fn;
public HandleImpl(Emitter obj, string ev, IListener fn)
{
this.obj = obj;
this.ev = ev;
this.fn = fn;
}
public void Destroy()
{
obj.Off(ev, fn);
}
}
public class ActionHandleImpl : IHandle
{
private Action fn;
public ActionHandleImpl(Action fn)
{
this.fn = fn;
}
public void Destroy()
{
fn();
}
}
public interface IHandle
{
void Destroy();
}
}
}

View File

@@ -0,0 +1,438 @@
using Newtonsoft.Json.Linq;
using System.Collections.Immutable;
using Quobject.EngineIoClientDotNet.ComponentEmitter;
using Quobject.EngineIoClientDotNet.Modules;
using Quobject.SocketIoClientDotNet.Modules;
using Quobject.SocketIoClientDotNet.Parser;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Quobject.SocketIoClientDotNet.Client
{
public class Socket : Emitter
{
public static readonly string EVENT_CONNECT = "connect";
public static readonly string EVENT_DISCONNECT = "disconnect";
public static readonly string EVENT_ERROR = "error";
public static readonly string EVENT_MESSAGE = "message";
public static readonly string EVENT_CONNECT_ERROR = Manager.EVENT_CONNECT_ERROR;
public static readonly string EVENT_CONNECT_TIMEOUT = Manager.EVENT_CONNECT_TIMEOUT;
public static readonly string EVENT_RECONNECT = Manager.EVENT_RECONNECT;
public static readonly string EVENT_RECONNECT_ERROR = Manager.EVENT_RECONNECT_ERROR;
public static readonly string EVENT_RECONNECT_FAILED = Manager.EVENT_RECONNECT_FAILED;
public static readonly string EVENT_RECONNECT_ATTEMPT = Manager.EVENT_RECONNECT_ATTEMPT;
public static readonly string EVENT_RECONNECTING = Manager.EVENT_RECONNECTING;
private static readonly List<string> Events = new List<string>()
{
EVENT_CONNECT,
EVENT_CONNECT_ERROR,
EVENT_CONNECT_TIMEOUT,
EVENT_DISCONNECT,
EVENT_ERROR,
EVENT_RECONNECT,
EVENT_RECONNECT_ATTEMPT,
EVENT_RECONNECT_FAILED,
EVENT_RECONNECT_ERROR,
EVENT_RECONNECTING
};
private bool Connected;
//private bool Disconnected = true;
private int Ids;
private string Nsp;
private Manager _io;
private ImmutableDictionary<int, IAck> Acks = ImmutableDictionary.Create<int, IAck>();
private ImmutableQueue<On.IHandle> Subs;
private ImmutableQueue<List<object>> ReceiveBuffer = ImmutableQueue.Create<List<object>>();
private ImmutableQueue<Parser.Packet> SendBuffer = ImmutableQueue.Create<Parser.Packet>();
public Socket(Manager io, string nsp)
{
_io = io;
Nsp = nsp;
this.SubEvents();
}
private void SubEvents()
{
Manager io = _io;
Subs = ImmutableQueue.Create<On.IHandle>();
Subs = Subs.Enqueue(Client.On.Create(io, Manager.EVENT_OPEN, new ListenerImpl(OnOpen)));
Subs = Subs.Enqueue(Client.On.Create(io, Manager.EVENT_PACKET, new ListenerImpl((data) => OnPacket((Packet)data))));
Subs = Subs.Enqueue(Client.On.Create(io, Manager.EVENT_CLOSE, new ListenerImpl((data) => OnClose((string)data))));
}
public Socket Open()
{
Task.Run(() =>
{
if (!Connected)
{
_io.Open();
if (_io.ReadyState == Manager.ReadyStateEnum.OPEN)
{
OnOpen();
}
}
});
return this;
}
public Socket Connect()
{
return this.Open();
}
public Socket Send(params object[] args)
{
Emit(EVENT_MESSAGE, args);
return this;
}
public override Emitter Emit(string eventString, params object[] args)
{
var log = LogManager.GetLogger(Global.CallerName());
if (Events.Contains(eventString))
{
base.Emit(eventString, args);
return this;
}
var _args = new List<object> { eventString };
_args.AddRange(args);
var ack = _args[_args.Count - 1] as IAck;
if (ack != null)
{
_args.RemoveAt(_args.Count - 1);
}
var jsonArgs = Parser.Packet.Args2JArray(_args);
var parserType = HasBinaryData.HasBinary(jsonArgs) ? Parser.Parser.BINARY_EVENT : Parser.Parser.EVENT;
var packet = new Packet(parserType, jsonArgs);
if (ack != null)
{
log.Info(string.Format("emitting packet with ack id {0}", Ids));
Acks = Acks.Add(Ids, ack);
packet.Id = Ids++;
}
if (Connected)
{
Packet(packet);
}
else
{
SendBuffer = SendBuffer.Enqueue(packet);
}
return this;
}
public Emitter Emit(string eventString, IAck ack, params object[] args)
{
var log = LogManager.GetLogger(Global.CallerName());
if (Events.Contains(eventString))
{
base.Emit(eventString, args);
return this;
}
var _args = new List<object> { eventString };
_args.AddRange(args);
var jsonArgs = Parser.Packet.Args2JArray(_args);
var parserType = HasBinaryData.HasBinary(jsonArgs) ? Parser.Parser.BINARY_EVENT : Parser.Parser.EVENT;
var packet = new Packet(parserType, jsonArgs);
log.Info(string.Format("emitting packet with ack id {0}", Ids));
Acks = Acks.Add(Ids, ack);
packet.Id = Ids++;
if (Connected)
{
Packet(packet);
}
else
{
SendBuffer = SendBuffer.Enqueue(packet);
}
return this;
}
public Emitter Emit(string eventString, Action ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object, object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object, object, object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object, object, object, object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public void Packet(Packet packet)
{
packet.Nsp = Nsp;
_io.Packet(packet);
}
private void OnOpen()
{
//var log = LogManager.GetLogger(Global.CallerName());
if (Nsp != "/")
{
Packet(new Packet(Parser.Parser.CONNECT));
}
}
private void OnClose(string reason)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("close ({0})", reason));
Connected = false;
Emit(EVENT_DISCONNECT, reason);
}
private void OnPacket(Packet packet)
{
if (Nsp != packet.Nsp)
{
return;
}
switch (packet.Type)
{
case Parser.Parser.CONNECT:
this.OnConnect();
break;
case Parser.Parser.EVENT:
this.OnEvent(packet);
break;
case Parser.Parser.BINARY_EVENT:
this.OnEvent(packet);
break;
case Parser.Parser.ACK:
this.OnAck(packet);
break;
case Parser.Parser.BINARY_ACK:
this.OnAck(packet);
break;
case Parser.Parser.DISCONNECT:
this.OnDisconnect();
break;
case Parser.Parser.ERROR:
this.Emit(EVENT_ERROR, packet.Data);
break;
}
}
private void OnEvent(Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
//var jarr =(string) ((JValue) packet.Data).Value;
//var job = JToken.Parse(jarr);
//var arr = job.ToArray();
//var args = job.Select(token => token.Value<string>()).Cast<object>().ToList();
var args = packet.GetDataAsList();
log.Info(string.Format("emitting event {0}", args));
if (packet.Id >= 0)
{
log.Info("attaching ack callback to event");
args.Add(new AckImp(this, packet.Id));
}
if (Connected)
{
var eventString = (string) args[0];
args.Remove(args[0]);
base.Emit(eventString, args.ToArray());
}
else
{
ReceiveBuffer = ReceiveBuffer.Enqueue(args);
}
}
private class AckImp : IAck
{
private Socket socket;
private int Id;
private readonly bool[] sent = new[] {false};
public AckImp(Socket socket, int id)
{
this.socket = socket;
this.Id = id;
}
public void Call(params object[] args)
{
if (sent[0])
{
return;
}
sent[0] = true;
var log = LogManager.GetLogger(Global.CallerName());
var jsonArgs = Parser.Packet.Args2JArray(args);
log.Info(string.Format("sending ack {0}", args.Length != 0 ? jsonArgs.ToString() : "null"));
var parserType = HasBinaryData.HasBinary(args) ? Parser.Parser.BINARY_ACK : Parser.Parser.ACK;
var packet = new Packet(parserType, jsonArgs);
packet.Id = Id;
socket.Packet(packet);
}
}
private void OnAck(Parser.Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("calling ack {0} with {1}", packet.Id, packet.Data));
var fn = Acks[packet.Id];
Acks = Acks.Remove(packet.Id);
var args = packet.GetDataAsList();
fn.Call(args.ToArray());
}
private void OnConnect()
{
Connected = true;
//Disconnected = false;
Emit(EVENT_CONNECT);
EmitBuffered();
}
private void EmitBuffered()
{
while (ReceiveBuffer.Count() > 0)
{
List<object> data;
ReceiveBuffer = ReceiveBuffer.Dequeue(out data);
var eventString = (string) data[0];
base.Emit(eventString, data.ToArray());
}
ReceiveBuffer = ReceiveBuffer.Clear();
while (SendBuffer.Count() > 0)
{
Packet packet;
SendBuffer = SendBuffer.Dequeue(out packet);
Packet(packet);
}
SendBuffer = SendBuffer.Clear();
}
private void OnDisconnect()
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("server disconnect ({0})", this.Nsp));
Destroy();
OnClose("io server disconnect");
}
private void Destroy()
{
foreach (var sub in Subs)
{
sub.Destroy();
}
Subs = Subs.Clear();
_io.Destroy(this);
}
public Socket Close()
{
var log = LogManager.GetLogger(Global.CallerName());
if (Connected)
{
log.Info(string.Format("performing disconnect ({0})", Nsp));
Packet(new Packet(Parser.Parser.DISCONNECT));
}
Destroy();
if (Connected)
{
OnClose("io client disconnect");
}
return this;
}
public Socket Disconnect()
{
return this.Close();
}
public Manager Io()
{
return _io;
}
private static IEnumerable<object> ToArray(JArray array)
{
int length = array.Count;
var data = new object[length];
for (int i = 0; i < length; i++)
{
object v;
try
{
v = array[i];
}
catch (Exception)
{
v = null;
}
data[i] = v;
}
return data;
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
namespace Quobject.SocketIoClientDotNet.Client
{
public class SocketIOException : Exception
{
public string Transport;
public object code;
public SocketIOException(string message)
: base(message)
{
}
public SocketIOException(Exception cause)
: base("", cause)
{
}
public SocketIOException(string message, Exception cause)
: base(message, cause)
{
}
}
}

View File

@@ -0,0 +1,413 @@

using Newtonsoft.Json.Linq;
using Quobject.SocketIoClientDotNet.Collections.Concurrent;
using Quobject.EngineIoClientDotNet.ComponentEmitter;
using Quobject.EngineIoClientDotNet.Modules;
using Quobject.SocketIoClientDotNet.Modules;
using Quobject.SocketIoClientDotNet.Parser;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Quobject.SocketIoClientDotNet.Client
{
public class Socket : Emitter
{
public static readonly string EVENT_CONNECT = "connect";
public static readonly string EVENT_DISCONNECT = "disconnect";
public static readonly string EVENT_ERROR = "error";
public static readonly string EVENT_MESSAGE = "message";
public static readonly string EVENT_CONNECT_ERROR = Manager.EVENT_CONNECT_ERROR;
public static readonly string EVENT_CONNECT_TIMEOUT = Manager.EVENT_CONNECT_TIMEOUT;
public static readonly string EVENT_RECONNECT = Manager.EVENT_RECONNECT;
public static readonly string EVENT_RECONNECT_ERROR = Manager.EVENT_RECONNECT_ERROR;
public static readonly string EVENT_RECONNECT_FAILED = Manager.EVENT_RECONNECT_FAILED;
public static readonly string EVENT_RECONNECT_ATTEMPT = Manager.EVENT_RECONNECT_ATTEMPT;
public static readonly string EVENT_RECONNECTING = Manager.EVENT_RECONNECTING;
private static readonly List<string> Events = new List<string>()
{
EVENT_CONNECT,
EVENT_CONNECT_ERROR,
EVENT_CONNECT_TIMEOUT,
EVENT_DISCONNECT,
EVENT_ERROR,
EVENT_RECONNECT,
EVENT_RECONNECT_ATTEMPT,
EVENT_RECONNECT_FAILED,
EVENT_RECONNECT_ERROR,
EVENT_RECONNECTING
};
private bool Connected;
//private bool Disconnected = true;
private int Ids;
private string Nsp;
private Manager _io;
private System.Collections.Concurrent.ConcurrentDictionary<int, IAck> Acks = new System.Collections.Concurrent.ConcurrentDictionary<int, IAck>();
private ConcurrentQueue<On.IHandle> Subs;
private ConcurrentQueue<List<object>> ReceiveBuffer = new ConcurrentQueue<List<object>>();
private ConcurrentQueue<Parser.Packet> SendBuffer = new ConcurrentQueue<Parser.Packet>();
public Socket(Manager io, string nsp)
{
_io = io;
Nsp = nsp;
this.SubEvents();
}
private void SubEvents()
{
Manager io = _io;
Subs = new ConcurrentQueue<On.IHandle>();
Subs.Enqueue(Client.On.Create(io, Manager.EVENT_OPEN, new ListenerImpl(OnOpen)));
Subs.Enqueue(Client.On.Create(io, Manager.EVENT_PACKET, new ListenerImpl((data) => OnPacket((Packet)data))));
Subs.Enqueue(Client.On.Create(io, Manager.EVENT_CLOSE, new ListenerImpl((data) => OnClose((string)data))));
}
public Socket Open()
{
Task.Run(() =>
{
if (!Connected)
{
_io.Open();
if (_io.ReadyState == Manager.ReadyStateEnum.OPEN)
{
OnOpen();
}
}
});
return this;
}
public Socket Connect()
{
return this.Open();
}
public Socket Send(params object[] args)
{
Emit(EVENT_MESSAGE, args);
return this;
}
public override Emitter Emit(string eventString, params object[] args)
{
var log = LogManager.GetLogger(Global.CallerName());
if (Events.Contains(eventString))
{
base.Emit(eventString, args);
return this;
}
var _args = new List<object> {eventString};
_args.AddRange(args);
var jsonArgs = Parser.Packet.Args2JArray(_args);
var parserType = HasBinaryData.HasBinary(jsonArgs) ? Parser.Parser.BINARY_EVENT : Parser.Parser.EVENT;
var packet = new Packet(parserType, jsonArgs);
var lastArg = _args[_args.Count - 1];
if (lastArg is IAck)
{
log.Info(string.Format("emitting packet with ack id {0}", Ids));
Acks.TryAdd(Ids, (IAck)lastArg);
jsonArgs = Parser.Packet.Remove(jsonArgs, jsonArgs.Count - 1);
packet.Data = jsonArgs;
packet.Id = Ids++;
}
if (Connected)
{
Packet(packet);
}
else
{
SendBuffer.Enqueue(packet);
}
return this;
}
public Emitter Emit(string eventString, IAck ack, params object[] args)
{
var log = LogManager.GetLogger(Global.CallerName());
var _args = new List<object> { eventString };
if (args != null)
{
_args.AddRange(args);
}
var jarray = new JArray(_args);
var packet = new Packet(Parser.Parser.EVENT, jarray);
log.Info(string.Format("emitting packet with ack id {0}", Ids));
Acks.TryAdd(Ids, ack);
packet.Id = Ids++;
Packet(packet);
return this;
}
public Emitter Emit(string eventString, Action ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object, object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object, object, object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public void Packet(Packet packet)
{
packet.Nsp = Nsp;
_io.Packet(packet);
}
private void OnOpen()
{
var log = LogManager.GetLogger(Global.CallerName());
if (Nsp != "/")
{
Packet(new Packet(Parser.Parser.CONNECT));
}
}
private void OnClose(string reason)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("close ({0})", reason));
Connected = false;
Emit(EVENT_DISCONNECT, reason);
}
private void OnPacket(Packet packet)
{
if (Nsp != packet.Nsp)
{
return;
}
switch (packet.Type)
{
case Parser.Parser.CONNECT:
this.OnConnect();
break;
case Parser.Parser.EVENT:
this.OnEvent(packet);
break;
case Parser.Parser.BINARY_EVENT:
this.OnEvent(packet);
break;
case Parser.Parser.ACK:
this.OnAck(packet);
break;
case Parser.Parser.BINARY_ACK:
this.OnAck(packet);
break;
case Parser.Parser.DISCONNECT:
this.OnDisconnect();
break;
case Parser.Parser.ERROR:
this.Emit(EVENT_ERROR, packet.Data);
break;
}
}
private void OnEvent(Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
//var jarr =(string) ((JValue) packet.Data).Value;
//var job = JToken.Parse(jarr);
//var arr = job.ToArray();
//var args = job.Select(token => token.Value<string>()).Cast<object>().ToList();
var args = packet.GetDataAsList();
log.Info(string.Format("emitting event {0}", args));
if (packet.Id >= 0)
{
log.Info("attaching ack callback to event");
args.Add(new AckImp(this, packet.Id));
}
if (Connected)
{
var eventString = (string) args[0];
args.Remove(args[0]);
base.Emit(eventString, args.ToArray());
}
else
{
ReceiveBuffer.Enqueue(args);
}
}
private class AckImp : IAck
{
private Socket socket;
private int Id;
private readonly bool[] sent = new[] {false};
public AckImp(Socket socket, int id)
{
this.socket = socket;
this.Id = id;
}
public void Call(params object[] args)
{
if (sent[0])
{
return;
}
sent[0] = true;
var log = LogManager.GetLogger(Global.CallerName());
var jsonArgs = Parser.Packet.Args2JArray(args);
log.Info(string.Format("sending ack {0}", args.Length != 0 ? jsonArgs.ToString() : "null"));
var parserType = HasBinaryData.HasBinary(args) ? Parser.Parser.BINARY_ACK : Parser.Parser.ACK;
var packet = new Packet(parserType, jsonArgs);
packet.Id = Id;
socket.Packet(packet);
}
}
private void OnAck(Parser.Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("calling ack {0} with {1}", packet.Id, packet.Data));
var fn = Acks[packet.Id];
IAck outRef;
Acks.TryRemove(packet.Id,out outRef );
var args = packet.GetDataAsList();
fn.Call(args.ToArray());
}
private void OnConnect()
{
Connected = true;
//Disconnected = false;
Emit(EVENT_CONNECT);
EmitBuffered();
}
private void EmitBuffered()
{
while (ReceiveBuffer.Count > 0)
{
List<object> data;
ReceiveBuffer.TryDequeue(out data);
var eventString = (string) data[0];
base.Emit(eventString, data.ToArray());
}
ReceiveBuffer.Clear();
while (SendBuffer.Count > 0)
{
Packet packet;
SendBuffer.TryDequeue(out packet);
Packet(packet);
}
SendBuffer.Clear();
}
private void OnDisconnect()
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("server disconnect ({0})", this.Nsp));
Destroy();
OnClose("io server disconnect");
}
private void Destroy()
{
foreach (var sub in Subs.GetEnumerator())
{
sub.Destroy();
}
Subs.Clear();
_io.Destroy(this);
}
public Socket Close()
{
if (!Connected)
{
return this;
}
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("performing disconnect ({0})", Nsp));
Packet(new Packet(Parser.Parser.DISCONNECT));
Destroy();
OnClose("io client disconnect");
return this;
}
public Socket Disconnect()
{
return this.Close();
}
public Manager Io()
{
return _io;
}
private static IEnumerable<object> ToArray(JArray array)
{
int length = array.Count;
var data = new object[length];
for (int i = 0; i < length; i++)
{
object v;
try
{
v = array[i];
}
catch (Exception)
{
v = null;
}
data[i] = v;
}
return data;
}
}
}

View File

@@ -0,0 +1,414 @@

using Newtonsoft.Json.Linq;
using Quobject.SocketIoClientDotNet.Collections.Concurrent;
using Quobject.EngineIoClientDotNet.ComponentEmitter;
using Quobject.EngineIoClientDotNet.Modules;
using Quobject.SocketIoClientDotNet.Modules;
using Quobject.SocketIoClientDotNet.Parser;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Quobject.SocketIoClientDotNet.Client
{
public class Socket : Emitter
{
public static readonly string EVENT_CONNECT = "connect";
public static readonly string EVENT_DISCONNECT = "disconnect";
public static readonly string EVENT_ERROR = "error";
public static readonly string EVENT_MESSAGE = "message";
public static readonly string EVENT_CONNECT_ERROR = Manager.EVENT_CONNECT_ERROR;
public static readonly string EVENT_CONNECT_TIMEOUT = Manager.EVENT_CONNECT_TIMEOUT;
public static readonly string EVENT_RECONNECT = Manager.EVENT_RECONNECT;
public static readonly string EVENT_RECONNECT_ERROR = Manager.EVENT_RECONNECT_ERROR;
public static readonly string EVENT_RECONNECT_FAILED = Manager.EVENT_RECONNECT_FAILED;
public static readonly string EVENT_RECONNECT_ATTEMPT = Manager.EVENT_RECONNECT_ATTEMPT;
public static readonly string EVENT_RECONNECTING = Manager.EVENT_RECONNECTING;
private static readonly List<string> Events = new List<string>()
{
EVENT_CONNECT,
EVENT_CONNECT_ERROR,
EVENT_CONNECT_TIMEOUT,
EVENT_DISCONNECT,
EVENT_ERROR,
EVENT_RECONNECT,
EVENT_RECONNECT_ATTEMPT,
EVENT_RECONNECT_FAILED,
EVENT_RECONNECT_ERROR,
EVENT_RECONNECTING
};
private bool Connected;
//private bool Disconnected = true;
private int Ids;
private string Nsp;
private Manager _io;
private System.Collections.Concurrent.ConcurrentDictionary<int, IAck> Acks = new System.Collections.Concurrent.ConcurrentDictionary<int, IAck>();
private ConcurrentQueue<On.IHandle> Subs;
private ConcurrentQueue<List<object>> ReceiveBuffer = new ConcurrentQueue<List<object>>();
private ConcurrentQueue<Parser.Packet> SendBuffer = new ConcurrentQueue<Parser.Packet>();
public Socket(Manager io, string nsp)
{
_io = io;
Nsp = nsp;
this.SubEvents();
}
private void SubEvents()
{
Manager io = _io;
Subs = new ConcurrentQueue<On.IHandle>();
Subs.Enqueue(Client.On.Create(io, Manager.EVENT_OPEN, new ListenerImpl(OnOpen)));
Subs.Enqueue(Client.On.Create(io, Manager.EVENT_PACKET, new ListenerImpl((data) => OnPacket((Packet)data))));
Subs.Enqueue(Client.On.Create(io, Manager.EVENT_CLOSE, new ListenerImpl((data) => OnClose((string)data))));
}
public Socket Open()
{
var t = new Task(() =>
{
if (!Connected)
{
_io.Open();
if (_io.ReadyState == Manager.ReadyStateEnum.OPEN)
{
OnOpen();
}
}
});
t.Start();
return this;
}
public Socket Connect()
{
return this.Open();
}
public Socket Send(params object[] args)
{
Emit(EVENT_MESSAGE, args);
return this;
}
public override Emitter Emit(string eventString, params object[] args)
{
var log = LogManager.GetLogger(Global.CallerName());
if (Events.Contains(eventString))
{
base.Emit(eventString, args);
return this;
}
var _args = new List<object> {eventString};
_args.AddRange(args);
var jsonArgs = Parser.Packet.Args2JArray(_args);
var parserType = HasBinaryData.HasBinary(jsonArgs) ? Parser.Parser.BINARY_EVENT : Parser.Parser.EVENT;
var packet = new Packet(parserType, jsonArgs);
var lastArg = _args[_args.Count - 1];
if (lastArg is IAck)
{
log.Info(string.Format("emitting packet with ack id {0}", Ids));
Acks.TryAdd(Ids, (IAck)lastArg);
jsonArgs = Parser.Packet.Remove(jsonArgs, jsonArgs.Count - 1);
packet.Data = jsonArgs;
packet.Id = Ids++;
}
if (Connected)
{
Packet(packet);
}
else
{
SendBuffer.Enqueue(packet);
}
return this;
}
public Emitter Emit(string eventString, IAck ack, params object[] args)
{
var log = LogManager.GetLogger(Global.CallerName());
var _args = new List<object> { eventString };
if (args != null)
{
_args.AddRange(args);
}
var jarray = new JArray(_args);
var packet = new Packet(Parser.Parser.EVENT, jarray);
log.Info(string.Format("emitting packet with ack id {0}", Ids));
Acks.TryAdd(Ids, ack);
packet.Id = Ids++;
Packet(packet);
return this;
}
public Emitter Emit(string eventString, Action ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object, object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public Emitter Emit(string eventString, Action<object, object, object> ack, params object[] args)
{
return Emit(eventString, new AckImpl(ack), args);
}
public void Packet(Packet packet)
{
packet.Nsp = Nsp;
_io.Packet(packet);
}
private void OnOpen()
{
var log = LogManager.GetLogger(Global.CallerName());
if (Nsp != "/")
{
Packet(new Packet(Parser.Parser.CONNECT));
}
}
private void OnClose(string reason)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("close ({0})", reason));
Connected = false;
Emit(EVENT_DISCONNECT, reason);
}
private void OnPacket(Packet packet)
{
if (Nsp != packet.Nsp)
{
return;
}
switch (packet.Type)
{
case Parser.Parser.CONNECT:
this.OnConnect();
break;
case Parser.Parser.EVENT:
this.OnEvent(packet);
break;
case Parser.Parser.BINARY_EVENT:
this.OnEvent(packet);
break;
case Parser.Parser.ACK:
this.OnAck(packet);
break;
case Parser.Parser.BINARY_ACK:
this.OnAck(packet);
break;
case Parser.Parser.DISCONNECT:
this.OnDisconnect();
break;
case Parser.Parser.ERROR:
this.Emit(EVENT_ERROR, packet.Data);
break;
}
}
private void OnEvent(Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
//var jarr =(string) ((JValue) packet.Data).Value;
//var job = JToken.Parse(jarr);
//var arr = job.ToArray();
//var args = job.Select(token => token.Value<string>()).Cast<object>().ToList();
var args = packet.GetDataAsList();
log.Info(string.Format("emitting event {0}", args));
if (packet.Id >= 0)
{
log.Info("attaching ack callback to event");
args.Add(new AckImp(this, packet.Id));
}
if (Connected)
{
var eventString = (string) args[0];
args.Remove(args[0]);
base.Emit(eventString, args.ToArray());
}
else
{
ReceiveBuffer.Enqueue(args);
}
}
private class AckImp : IAck
{
private Socket socket;
private int Id;
private readonly bool[] sent = new[] {false};
public AckImp(Socket socket, int id)
{
this.socket = socket;
this.Id = id;
}
public void Call(params object[] args)
{
if (sent[0])
{
return;
}
sent[0] = true;
var log = LogManager.GetLogger(Global.CallerName());
var jsonArgs = Parser.Packet.Args2JArray(args);
log.Info(string.Format("sending ack {0}", args.Length != 0 ? jsonArgs.ToString() : "null"));
var parserType = HasBinaryData.HasBinary(args) ? Parser.Parser.BINARY_ACK : Parser.Parser.ACK;
var packet = new Packet(parserType, jsonArgs);
packet.Id = Id;
socket.Packet(packet);
}
}
private void OnAck(Parser.Packet packet)
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("calling ack {0} with {1}", packet.Id, packet.Data));
var fn = Acks[packet.Id];
IAck outRef;
Acks.TryRemove(packet.Id,out outRef );
var args = packet.GetDataAsList();
fn.Call(args.ToArray());
}
private void OnConnect()
{
Connected = true;
//Disconnected = false;
Emit(EVENT_CONNECT);
EmitBuffered();
}
private void EmitBuffered()
{
while (ReceiveBuffer.Count > 0)
{
List<object> data;
ReceiveBuffer.TryDequeue(out data);
var eventString = (string) data[0];
base.Emit(eventString, data.ToArray());
}
ReceiveBuffer.Clear();
while (SendBuffer.Count > 0)
{
Packet packet;
SendBuffer.TryDequeue(out packet);
Packet(packet);
}
SendBuffer.Clear();
}
private void OnDisconnect()
{
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("server disconnect ({0})", this.Nsp));
Destroy();
OnClose("io server disconnect");
}
private void Destroy()
{
foreach (var sub in Subs.GetEnumerator())
{
sub.Destroy();
}
Subs.Clear();
_io.Destroy(this);
}
public Socket Close()
{
if (!Connected)
{
return this;
}
var log = LogManager.GetLogger(Global.CallerName());
log.Info(string.Format("performing disconnect ({0})", Nsp));
Packet(new Packet(Parser.Parser.DISCONNECT));
Destroy();
OnClose("io client disconnect");
return this;
}
public Socket Disconnect()
{
return this.Close();
}
public Manager Io()
{
return _io;
}
private static IEnumerable<object> ToArray(JArray array)
{
int length = array.Count;
var data = new object[length];
for (int i = 0; i < length; i++)
{
object v;
try
{
v = array[i];
}
catch (Exception)
{
v = null;
}
data[i] = v;
}
return data;
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
namespace Quobject.SocketIoClientDotNet.Client
{
public class Url
{
private Url() { }
public static Uri Parse(string uri)
{
if (uri.StartsWith("//"))
{
uri = "http:" + uri;
}
var result = new Uri(uri);
return result;
}
public static string ExtractId(string url)
{
return ExtractId(new Uri(url));
}
public static string ExtractId(Uri uri)
{
var protocol = uri.Scheme;
int port = uri.Port;
if (port == -1)
{
if (uri.Scheme.StartsWith("https"))
{
port = 443;
}else if (uri.Scheme.StartsWith("http"))
{
port = 80;
}
}
return string.Format("{0}://{1}:{2}", protocol, uri.Host , port);
}
}
}