mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-20 21:02:55 +00:00
Initial v2 changes
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CryptoExchange.Net.Interfaces;
|
||||
using CryptoExchange.Net.Logging;
|
||||
using SuperSocket.ClientEngine;
|
||||
using SuperSocket.ClientEngine.Proxy;
|
||||
using WebSocket4Net;
|
||||
|
||||
namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
public class BaseSocket: IWebsocket
|
||||
{
|
||||
internal static int lastStreamId;
|
||||
private static readonly object streamIdLock = new object();
|
||||
|
||||
protected WebSocket socket;
|
||||
protected Log log;
|
||||
protected object socketLock = new object();
|
||||
protected DateTime? lostTime = null;
|
||||
|
||||
protected readonly List<Action<Exception>> errorHandlers = new List<Action<Exception>>();
|
||||
protected readonly List<Action> openHandlers = new List<Action>();
|
||||
protected readonly List<Action> closeHandlers = new List<Action>();
|
||||
protected readonly List<Action<string>> messageHandlers = new List<Action<string>>();
|
||||
|
||||
public int Id { get; }
|
||||
public DateTime? DisconnectTime { get; set; }
|
||||
public bool ShouldReconnect { get; set; }
|
||||
public string Url { get; }
|
||||
public bool IsClosed => socket.State == WebSocketState.Closed;
|
||||
public bool IsOpen => socket.State == WebSocketState.Open;
|
||||
public Func<byte[], string> DataInterpreter { get; set; }
|
||||
|
||||
public bool PingConnection
|
||||
{
|
||||
get => socket.EnableAutoSendPing;
|
||||
set => socket.EnableAutoSendPing = value;
|
||||
}
|
||||
|
||||
public TimeSpan PingInterval
|
||||
{
|
||||
get => TimeSpan.FromSeconds(socket.AutoSendPingInterval);
|
||||
set => socket.AutoSendPingInterval = (int) Math.Round(value.TotalSeconds);
|
||||
}
|
||||
|
||||
public WebSocketState SocketState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (socket == null)
|
||||
return WebSocketState.None;
|
||||
return socket.State;
|
||||
}
|
||||
}
|
||||
|
||||
public BaseSocket(Log log, string url):this(log, url, new Dictionary<string, string>(), new Dictionary<string, string>())
|
||||
{
|
||||
}
|
||||
|
||||
public BaseSocket(Log log, string url, IDictionary<string, string> cookies, IDictionary<string, string> headers)
|
||||
{
|
||||
Id = NextStreamId();
|
||||
this.log = log;
|
||||
Url = url;
|
||||
socket = new WebSocket(url, cookies: cookies.ToList(), customHeaderItems: headers.ToList())
|
||||
{
|
||||
EnableAutoSendPing = true,
|
||||
AutoSendPingInterval = 10
|
||||
};
|
||||
socket.Opened += (o, s) => Handle(openHandlers);
|
||||
socket.Closed += (o, s) => Handle(closeHandlers);
|
||||
socket.Error += (o, s) => Handle(errorHandlers, s.Exception);
|
||||
socket.MessageReceived += (o, s) => Handle(messageHandlers, s.Message);
|
||||
socket.DataReceived += (o, s) => HandleByteData(s.Data);
|
||||
}
|
||||
|
||||
private void HandleByteData(byte[] data)
|
||||
{
|
||||
var message = DataInterpreter(data);
|
||||
Handle(messageHandlers, message);
|
||||
}
|
||||
|
||||
public event Action OnClose
|
||||
{
|
||||
add => closeHandlers.Add(value);
|
||||
remove => closeHandlers.Remove(value);
|
||||
}
|
||||
public event Action<string> OnMessage
|
||||
{
|
||||
add => messageHandlers.Add(value);
|
||||
remove => messageHandlers.Remove(value);
|
||||
}
|
||||
public event Action<Exception> OnError
|
||||
{
|
||||
add => errorHandlers.Add(value);
|
||||
remove => errorHandlers.Remove(value);
|
||||
}
|
||||
public event Action OnOpen
|
||||
{
|
||||
add => openHandlers.Add(value);
|
||||
remove => openHandlers.Remove(value);
|
||||
}
|
||||
|
||||
protected static void Handle(List<Action> handlers)
|
||||
{
|
||||
foreach (var handle in new List<Action>(handlers))
|
||||
handle?.Invoke();
|
||||
}
|
||||
|
||||
protected void Handle<T>(List<Action<T>> handlers, T data)
|
||||
{
|
||||
foreach (var handle in new List<Action<T>>(handlers))
|
||||
handle?.Invoke(data);
|
||||
}
|
||||
|
||||
public async Task Close()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
lock (socketLock)
|
||||
{
|
||||
if (socket == null || IsClosed)
|
||||
{
|
||||
log.Write(LogVerbosity.Debug, $"Socket {Id} was already closed/disposed");
|
||||
return;
|
||||
}
|
||||
|
||||
var waitLock = new object();
|
||||
log.Write(LogVerbosity.Debug, $"Socket {Id} closing");
|
||||
ManualResetEvent evnt = new ManualResetEvent(false);
|
||||
var handler = new EventHandler((o, a) =>
|
||||
{
|
||||
lock(waitLock)
|
||||
evnt?.Set();
|
||||
});
|
||||
socket.Closed += handler;
|
||||
socket.Close();
|
||||
evnt.WaitOne(2000);
|
||||
lock (waitLock)
|
||||
{
|
||||
socket.Closed -= handler;
|
||||
evnt.Dispose();
|
||||
evnt = null;
|
||||
}
|
||||
log.Write(LogVerbosity.Debug, $"Socket {Id} closed");
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Send(string data)
|
||||
{
|
||||
socket.Send(data);
|
||||
}
|
||||
|
||||
public async Task<bool> Connect()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
bool connected;
|
||||
lock (socketLock)
|
||||
{
|
||||
log.Write(LogVerbosity.Debug, $"Socket {Id} connecting");
|
||||
var waitLock = new object();
|
||||
ManualResetEvent evnt = new ManualResetEvent(false);
|
||||
var handler = new EventHandler((o, a) =>
|
||||
{
|
||||
lock (waitLock)
|
||||
evnt?.Set();
|
||||
});
|
||||
var errorHandler = new EventHandler<ErrorEventArgs>((o, a) =>
|
||||
{
|
||||
lock(waitLock)
|
||||
evnt?.Set();
|
||||
});
|
||||
socket.Opened += handler;
|
||||
socket.Closed += handler;
|
||||
socket.Error += errorHandler;
|
||||
socket.Open();
|
||||
evnt.WaitOne(TimeSpan.FromSeconds(15));
|
||||
lock (waitLock)
|
||||
{
|
||||
socket.Opened -= handler;
|
||||
socket.Closed -= handler;
|
||||
socket.Error -= errorHandler;
|
||||
evnt.Dispose();
|
||||
evnt = null;
|
||||
}
|
||||
connected = socket.State == WebSocketState.Open;
|
||||
if (connected)
|
||||
log.Write(LogVerbosity.Debug, $"Socket {Id} connected");
|
||||
else
|
||||
log.Write(LogVerbosity.Debug, $"Socket {Id} connection failed, state: " + socket.State);
|
||||
}
|
||||
|
||||
if (socket.State == WebSocketState.Connecting)
|
||||
Close().Wait();
|
||||
|
||||
return connected;
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void SetEnabledSslProtocols(SslProtocols protocols)
|
||||
{
|
||||
socket.Security.EnabledSslProtocols = protocols;
|
||||
}
|
||||
|
||||
public void SetProxy(string host, int port)
|
||||
{
|
||||
IPAddress address;
|
||||
socket.Proxy = IPAddress.TryParse(host, out address)
|
||||
? new HttpConnectProxy(new IPEndPoint(address, port))
|
||||
: new HttpConnectProxy(new DnsEndPoint(host, port));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (socketLock)
|
||||
{
|
||||
if (socket != null)
|
||||
log.Write(LogVerbosity.Debug, $"Socket {Id} sisposing websocket");
|
||||
|
||||
socket?.Dispose();
|
||||
socket = null;
|
||||
|
||||
errorHandlers.Clear();
|
||||
openHandlers.Clear();
|
||||
closeHandlers.Clear();
|
||||
messageHandlers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private int NextStreamId()
|
||||
{
|
||||
lock (streamIdLock)
|
||||
{
|
||||
lastStreamId++;
|
||||
return lastStreamId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using CryptoExchange.Net.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
public class SocketSubscription
|
||||
{
|
||||
public event Action ConnectionLost;
|
||||
public event Action ConnectionRestored;
|
||||
|
||||
public IWebsocket Socket { get; set; }
|
||||
public object Request { get; set; }
|
||||
|
||||
private bool lostTriggered;
|
||||
|
||||
public SocketSubscription(IWebsocket socket)
|
||||
{
|
||||
Socket = socket;
|
||||
|
||||
Socket.OnClose += () =>
|
||||
{
|
||||
if (lostTriggered)
|
||||
return;
|
||||
|
||||
lostTriggered = true;
|
||||
if (Socket.ShouldReconnect)
|
||||
ConnectionLost?.Invoke();
|
||||
};
|
||||
Socket.OnOpen += () =>
|
||||
{
|
||||
lostTriggered = false;
|
||||
if (Socket.DisconnectTime != null)
|
||||
ConnectionRestored?.Invoke();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using CryptoExchange.Net.Interfaces;
|
||||
using CryptoExchange.Net.Logging;
|
||||
|
||||
namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
public class WebsocketFactory : IWebsocketFactory
|
||||
{
|
||||
public IWebsocket CreateWebsocket(Log log, string url)
|
||||
{
|
||||
return new BaseSocket(log, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user