1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-12 17:03:10 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
JKorf ea9375d582 Updated version 2022-06-12 15:36:04 +02:00
JKorf 2cf3c93e5e Cleanup 2022-06-12 15:35:35 +02:00
JKorf ca888d8e41 Updated version 2022-06-12 15:31:03 +02:00
JKorf 2040b1c175 Fixed proxy setting not used on reconnecting socket 2022-06-12 15:26:11 +02:00
JKorf d451c18821 No longer waiting for timesyncing to complete when it's not the first request 2022-06-12 15:21:22 +02:00
JKorf c13dfa4461 Updated socket reconnection 2022-06-12 15:10:10 +02:00
JKorf c2080ef75f Made MaxSocketConnections a setting, added support for changing log settings after creating client 2022-06-11 13:31:39 +02:00
Jan Korf 6b252e8024 Update TestSocket.cs 2022-05-24 22:36:55 +02:00
13 changed files with 274 additions and 91 deletions
@@ -135,7 +135,7 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
throw new NotImplementedException();
}
protected override TimeSyncInfo GetTimeSyncInfo()
public override TimeSyncInfo GetTimeSyncInfo()
{
throw new NotImplementedException();
}
@@ -161,7 +161,7 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
throw new NotImplementedException();
}
protected override TimeSyncInfo GetTimeSyncInfo()
public override TimeSyncInfo GetTimeSyncInfo()
{
throw new NotImplementedException();
}
@@ -40,6 +40,8 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
public Uri Uri => new Uri("");
public TimeSpan KeepAliveInterval { get; set; }
public static int lastId = 0;
public static object lastIdLock = new object();
+11
View File
@@ -63,6 +63,7 @@ namespace CryptoExchange.Net
log = new Log(name);
log.UpdateWriters(options.LogWriters);
log.Level = options.LogLevel;
options.OnLoggingChanged += HandleLogConfigChange;
ClientOptions = options;
@@ -282,12 +283,22 @@ namespace CryptoExchange.Net
}
}
/// <summary>
/// Handle a change in the client options log config
/// </summary>
private void HandleLogConfigChange()
{
log.UpdateWriters(ClientOptions.LogWriters);
log.Level = ClientOptions.LogLevel;
}
/// <summary>
/// Dispose
/// </summary>
public virtual void Dispose()
{
log.Write(LogLevel.Debug, "Disposing client");
ClientOptions.OnLoggingChanged -= HandleLogConfigChange;
foreach (var client in ApiClients)
client.Dispose();
}
+10 -4
View File
@@ -172,11 +172,17 @@ namespace CryptoExchange.Net
if (signed)
{
var syncTimeResult = await apiClient.SyncTimeAsync().ConfigureAwait(false);
if (!syncTimeResult)
var syncTask = apiClient.SyncTimeAsync();
var timeSyncInfo = apiClient.GetTimeSyncInfo();
if (timeSyncInfo.TimeSyncState.LastSyncTime == default)
{
log.Write(LogLevel.Debug, $"[{requestId}] Failed to sync time, aborting request: " + syncTimeResult.Error);
return syncTimeResult.As<IRequest>(default);
// Initially with first request we'll need to wait for the time syncing, if it's not the first request we can just continue
var syncTimeResult = await syncTask.ConfigureAwait(false);
if (!syncTimeResult)
{
log.Write(LogLevel.Debug, $"[{requestId}] Failed to sync time, aborting request: " + syncTimeResult.Error);
return syncTimeResult.As<IRequest>(default);
}
}
}
+47 -27
View File
@@ -36,10 +36,6 @@ namespace CryptoExchange.Net
/// </summary>
protected internal readonly SemaphoreSlim semaphoreSlim = new(1);
/// <summary>
/// The max amount of concurrent socket connections
/// </summary>
protected int MaxSocketConnections { get; set; } = 9999;
/// <summary>
/// Keep alive interval for websocket connection
/// </summary>
protected TimeSpan KeepAliveInterval { get; set; } = TimeSpan.FromSeconds(10);
@@ -96,6 +92,20 @@ namespace CryptoExchange.Net
}
}
/// <inheritdoc />
public int CurrentConnections => socketConnections.Count;
/// <inheritdoc />
public int CurrentSubscriptions
{
get
{
if (!socketConnections.Any())
return 0;
return socketConnections.Sum(s => s.Value.SubscriptionCount);
}
}
/// <summary>
/// Client options
/// </summary>
@@ -168,7 +178,7 @@ namespace CryptoExchange.Net
return new CallResult<UpdateSubscription>(new InvalidOperationError("Client disposed, can't subscribe"));
SocketConnection socketConnection;
SocketSubscription subscription;
SocketSubscription? subscription;
var released = false;
// Wait for a semaphore here, so we only connect 1 socket at a time.
// This is necessary for being able to see if connections can be combined
@@ -183,23 +193,34 @@ namespace CryptoExchange.Net
try
{
// Get a new or existing socket connection
socketConnection = GetSocketConnection(apiClient, url, authenticated);
// Add a subscription on the socket connection
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler);
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
while (true)
{
// Only 1 subscription per connection, so no need to wait for connection since a new subscription will create a new connection anyway
semaphoreSlim.Release();
released = true;
// Get a new or existing socket connection
socketConnection = GetSocketConnection(apiClient, url, authenticated);
// Add a subscription on the socket connection
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler);
if (subscription == null)
{
log.Write(LogLevel.Trace, $"Socket {socketConnection.SocketId} failed to add subscription, retrying on different connection");
continue;
}
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
{
// Only 1 subscription per connection, so no need to wait for connection since a new subscription will create a new connection anyway
semaphoreSlim.Release();
released = true;
}
var needsConnecting = !socketConnection.Connected;
var connectResult = await ConnectIfNeededAsync(socketConnection, authenticated).ConfigureAwait(false);
if (!connectResult)
return new CallResult<UpdateSubscription>(connectResult.Error!);
break;
}
var needsConnecting = !socketConnection.Connected;
var connectResult = await ConnectIfNeededAsync(socketConnection, authenticated).ConfigureAwait(false);
if (!connectResult)
return new CallResult<UpdateSubscription>(connectResult.Error!);
}
finally
{
@@ -447,9 +468,6 @@ namespace CryptoExchange.Net
/// <param name="message"></param>
/// <returns></returns>
protected internal virtual JToken ProcessTokenData(JToken message)
{
return message;
}
@@ -464,7 +482,7 @@ namespace CryptoExchange.Net
/// <param name="connection">The socket connection the handler is on</param>
/// <param name="dataHandler">The handler of the data received</param>
/// <returns></returns>
protected virtual SocketSubscription AddSubscription<T>(object? request, string? identifier, bool userSubscription, SocketConnection connection, Action<DataEvent<T>> dataHandler)
protected virtual SocketSubscription? AddSubscription<T>(object? request, string? identifier, bool userSubscription, SocketConnection connection, Action<DataEvent<T>> dataHandler)
{
void InternalHandler(MessageEvent messageEvent)
{
@@ -488,7 +506,8 @@ namespace CryptoExchange.Net
var subscription = request == null
? SocketSubscription.CreateForIdentifier(NextId(), identifier!, userSubscription, InternalHandler)
: SocketSubscription.CreateForRequest(NextId(), request, userSubscription, InternalHandler);
connection.AddSubscription(subscription);
if (!connection.AddSubscription(subscription))
return null;
return subscription;
}
@@ -514,13 +533,14 @@ namespace CryptoExchange.Net
/// <returns></returns>
protected virtual SocketConnection GetSocketConnection(SocketApiClient apiClient, string address, bool authenticated)
{
var socketResult = socketConnections.Where(s => s.Value.Uri.ToString().TrimEnd('/') == address.TrimEnd('/')
var socketResult = socketConnections.Where(s => (s.Value.Status == SocketConnection.SocketStatus.None || s.Value.Status == SocketConnection.SocketStatus.Connected)
&& s.Value.Uri.ToString().TrimEnd('/') == address.TrimEnd('/')
&& (s.Value.ApiClient.GetType() == apiClient.GetType())
&& (s.Value.Authenticated == authenticated || !authenticated) && s.Value.Connected).OrderBy(s => s.Value.SubscriptionCount).FirstOrDefault();
var result = socketResult.Equals(default(KeyValuePair<int, SocketConnection>)) ? null : socketResult.Value;
if (result != null)
{
if (result.SubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget || (socketConnections.Count >= MaxSocketConnections && socketConnections.All(s => s.Value.SubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
if (result.SubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget || (socketConnections.Count >= ClientOptions.MaxSocketConnections && socketConnections.All(s => s.Value.SubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
{
// Use existing socket if it has less than target connections OR it has the least connections and we can't make new
return result;
+1 -1
View File
@@ -18,7 +18,7 @@ namespace CryptoExchange.Net
/// Get time sync info for an API client
/// </summary>
/// <returns></returns>
protected abstract TimeSyncInfo GetTimeSyncInfo();
public abstract TimeSyncInfo GetTimeSyncInfo();
/// <summary>
/// Get time offset for an API client
+4 -4
View File
@@ -6,16 +6,16 @@
<PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors>
<Description>A base package for implementing cryptocurrency API's</Description>
<PackageVersion>5.1.11</PackageVersion>
<AssemblyVersion>5.1.11</AssemblyVersion>
<FileVersion>5.1.11</FileVersion>
<PackageVersion>5.1.12</PackageVersion>
<AssemblyVersion>5.1.12</AssemblyVersion>
<FileVersion>5.1.12</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/JKorf/CryptoExchange.Net.git</RepositoryUrl>
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
<NeutralLanguage>en</NeutralLanguage>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>5.1.11 - Added KeepAliveInterval setting, Fixed port not being copied when setting parameters on request, Fixed inconsistent PackageReference casing in csproj</PackageReleaseNotes>
<PackageReleaseNotes>5.1.12 - Changed time sync so requests no longer wait for it to complete unless it's the first time, Made log client options changable after client creation, Fixed proxy setting not used when reconnecting socket, Changed MaxSocketConnections to a client options, Updated socket reconnection logic</PackageReleaseNotes>
<Nullable>enable</Nullable>
<LangVersion>9.0</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
@@ -27,6 +27,16 @@ namespace CryptoExchange.Net.Interfaces
/// </summary>
public double IncomingKbps { get; }
/// <summary>
/// The current amount of connections to the API from this client. A connection can have multiple subscriptions.
/// </summary>
public int CurrentConnections { get; }
/// <summary>
/// The current amount of subscriptions running from the client
/// </summary>
public int CurrentSubscriptions { get; }
/// <summary>
/// Unsubscribe from a stream using the subscription id received when starting the subscription
/// </summary>
+15 -9
View File
@@ -26,6 +26,8 @@ namespace CryptoExchange.Net.Logging
/// </summary>
public string ClientName { get; set; }
private readonly object _lock = new object();
/// <summary>
/// ctor
/// </summary>
@@ -42,7 +44,8 @@ namespace CryptoExchange.Net.Logging
/// <param name="textWriters"></param>
public void UpdateWriters(List<ILogger> textWriters)
{
writers = textWriters;
lock (_lock)
writers = textWriters;
}
/// <summary>
@@ -56,16 +59,19 @@ namespace CryptoExchange.Net.Logging
return;
var logMessage = $"{ClientName,-10} | {message}";
foreach (var writer in writers.ToList())
lock (_lock)
{
try
foreach (var writer in writers)
{
writer.Log(logLevel, logMessage);
}
catch (Exception e)
{
// Can't write to the logging so where else to output..
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Failed to write log to writer {writer.GetType()}: " + e.ToLogString());
try
{
writer.Log(logLevel, logMessage);
}
catch (Exception e)
{
// Can't write to the logging so where else to output..
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Failed to write log to writer {writer.GetType()}: " + e.ToLogString());
}
}
}
}
+29 -3
View File
@@ -14,15 +14,35 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public class BaseOptions
{
internal event Action? OnLoggingChanged;
private LogLevel _logLevel = LogLevel.Information;
/// <summary>
/// The minimum log level to output
/// </summary>
public LogLevel LogLevel { get; set; } = LogLevel.Information;
public LogLevel LogLevel
{
get => _logLevel;
set
{
_logLevel = value;
OnLoggingChanged?.Invoke();
}
}
private List<ILogger> _logWriters = new List<ILogger> { new DebugLogger() };
/// <summary>
/// The log writers
/// </summary>
public List<ILogger> LogWriters { get; set; } = new List<ILogger> { new DebugLogger() };
public List<ILogger> LogWriters
{
get => _logWriters;
set
{
_logWriters = value;
OnLoggingChanged?.Invoke();
}
}
/// <summary>
/// If true, the CallResult and DataEvent objects will also include the originally received json data in the OriginalData property
@@ -189,6 +209,11 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public int? SocketSubscriptionsCombineTarget { get; set; }
/// <summary>
/// The max amount of connections to make to the server. Can be used for API's which only allow a certain number of connections. Changing this to a high value might cause issues.
/// </summary>
public int? MaxSocketConnections { get; set; }
/// <summary>
/// ctor
/// </summary>
@@ -213,12 +238,13 @@ namespace CryptoExchange.Net.Objects
SocketResponseTimeout = baseOptions.SocketResponseTimeout;
SocketNoDataTimeout = baseOptions.SocketNoDataTimeout;
SocketSubscriptionsCombineTarget = baseOptions.SocketSubscriptionsCombineTarget;
MaxSocketConnections = baseOptions.MaxSocketConnections;
}
/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()}, AutoReconnect: {AutoReconnect}, ReconnectInterval: {ReconnectInterval}, MaxReconnectTries: {MaxReconnectTries}, MaxResubscribeTries: {MaxResubscribeTries}, MaxConcurrentResubscriptionsPerSocket: {MaxConcurrentResubscriptionsPerSocket}, SocketResponseTimeout: {SocketResponseTimeout:c}, SocketNoDataTimeout: {SocketNoDataTimeout}, SocketSubscriptionsCombineTarget: {SocketSubscriptionsCombineTarget}";
return $"{base.ToString()}, AutoReconnect: {AutoReconnect}, ReconnectInterval: {ReconnectInterval}, MaxReconnectTries: {MaxReconnectTries}, MaxResubscribeTries: {MaxResubscribeTries}, MaxConcurrentResubscriptionsPerSocket: {MaxConcurrentResubscriptionsPerSocket}, SocketResponseTimeout: {SocketResponseTimeout:c}, SocketNoDataTimeout: {SocketNoDataTimeout}, SocketSubscriptionsCombineTarget: {SocketSubscriptionsCombineTarget}, MaxSocketConnections: {MaxSocketConnections}";
}
}
@@ -31,9 +31,12 @@ namespace CryptoExchange.Net.Sockets
private readonly IDictionary<string, string> cookies;
private readonly IDictionary<string, string> headers;
private CancellationTokenSource _ctsSource;
private ApiProxy? _proxy;
private readonly List<DateTime> _outgoingMessages;
private DateTime _lastReceivedMessagesUpdate;
private bool _closed;
private bool _disposed;
/// <summary>
/// Received messages, the size and the timstamp
@@ -207,6 +210,8 @@ namespace CryptoExchange.Net.Sockets
/// <inheritdoc />
public virtual void SetProxy(ApiProxy proxy)
{
_proxy = proxy;
if (!Uri.TryCreate($"{proxy.Host}:{proxy.Port}", UriKind.Absolute, out var uri))
throw new ArgumentException("Proxy settings invalid, {proxy.Host}:{proxy.Port} not a valid URI", nameof(proxy));
@@ -279,6 +284,10 @@ namespace CryptoExchange.Net.Sockets
/// <returns></returns>
private async Task CloseInternalAsync()
{
if (_closed || _disposed)
return;
_closed = true;
_ctsSource.Cancel();
_sendEvent.Set();
@@ -291,6 +300,15 @@ namespace CryptoExchange.Net.Sockets
catch(Exception)
{ } // Can sometimes throw an exception when socket is in aborted state due to timing
}
else if(_socket.State == WebSocketState.CloseReceived)
{
try
{
await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", default).ConfigureAwait(false);
}
catch (Exception)
{ } // Can sometimes throw an exception when socket is in aborted state due to timing
}
log.Write(LogLevel.Debug, $"Socket {Id} closed");
Handle(closeHandlers);
}
@@ -300,7 +318,11 @@ namespace CryptoExchange.Net.Sockets
/// </summary>
public void Dispose()
{
if (_disposed)
return;
log.Write(LogLevel.Debug, $"Socket {Id} disposing");
_disposed = true;
_socket.Dispose();
_ctsSource.Dispose();
@@ -320,6 +342,9 @@ namespace CryptoExchange.Net.Sockets
while (_sendBuffer.TryDequeue(out _)) { } // Clear send buffer
_socket = CreateSocket();
if (_proxy != null)
SetProxy(_proxy);
_closed = false;
}
/// <summary>
+105 -41
View File
@@ -139,10 +139,26 @@ namespace CryptoExchange.Net.Sockets
private readonly BaseSocketClient socketClient;
private readonly List<PendingRequest> pendingRequests;
private Task? _socketProcessReconnectTask;
private Task? _socketProcessTask;
private Task? _socketReconnectTask;
private readonly AsyncResetEvent _reconnectWaitEvent;
private SocketStatus _status;
/// <summary>
/// Status of the socket connection
/// </summary>
public SocketStatus Status
{
get => _status;
private set
{
var oldStatus = _status;
_status = value;
log.Write(LogLevel.Trace, $"Socket {SocketId} status changed from {oldStatus} to {_status}");
}
}
/// <summary>
/// The underlying websocket
/// </summary>
@@ -165,9 +181,13 @@ namespace CryptoExchange.Net.Sockets
subscriptions = new List<SocketSubscription>();
_socket = socket;
_reconnectWaitEvent = new AsyncResetEvent(false, true);
_socket.Timeout = client.ClientOptions.SocketNoDataTimeout;
_socket.OnMessage += ProcessMessage;
_socket.OnOpen += SocketOnOpen;
_socket.OnClose += () => _reconnectWaitEvent.Set();
}
/// <summary>
@@ -178,7 +198,11 @@ namespace CryptoExchange.Net.Sockets
{
var connected = await _socket.ConnectAsync().ConfigureAwait(false);
if (connected)
StartProcessingTask();
{
Status = SocketStatus.Connected;
_socketReconnectTask = ReconnectWatcherAsync();
_socketProcessTask = _socket.ProcessAsync();
}
return connected;
}
@@ -207,6 +231,9 @@ namespace CryptoExchange.Net.Sockets
/// <returns></returns>
public async Task CloseAsync()
{
if (Status == SocketStatus.Closed || Status == SocketStatus.Disposed)
return;
ShouldReconnect = false;
if (socketClient.socketConnections.ContainsKey(SocketId))
socketClient.socketConnections.TryRemove(SocketId, out _);
@@ -220,24 +247,13 @@ namespace CryptoExchange.Net.Sockets
}
}
if (_status == SocketStatus.Reconnecting)
{
// Wait for reconnect task to finish
log.Write(LogLevel.Trace, "In reconnecting state, waiting for reconnecting to end");
if (_socketProcessReconnectTask != null)
await _socketProcessReconnectTask.ConfigureAwait(false);
await _socket.CloseAsync().ConfigureAwait(false);
}
else
{
// Close before waiting for process task to finish
await _socket.CloseAsync().ConfigureAwait(false);
if (_socketProcessReconnectTask != null)
await _socketProcessReconnectTask.ConfigureAwait(false);
}
while (Status == SocketStatus.Reconnecting)
// Wait for reconnecting to finish
await Task.Delay(100).ConfigureAwait(false);
await _socket.CloseAsync().ConfigureAwait(false);
if(_socketProcessTask != null)
await _socketProcessTask.ConfigureAwait(false);
_socket.Dispose();
}
@@ -248,39 +264,40 @@ namespace CryptoExchange.Net.Sockets
/// <returns></returns>
public async Task CloseAsync(SocketSubscription subscription)
{
if (!_socket.IsOpen || _status == SocketStatus.Disposed)
if (Status == SocketStatus.Closing || Status == SocketStatus.Closed || Status == SocketStatus.Disposed)
return;
log.Write(LogLevel.Trace, $"Socket {SocketId} closing subscription {subscription.Id}");
if (subscription.CancellationTokenRegistration.HasValue)
subscription.CancellationTokenRegistration.Value.Dispose();
if (subscription.Confirmed)
if (subscription.Confirmed && _socket.IsOpen)
await socketClient.UnsubscribeAsync(this, subscription).ConfigureAwait(false);
bool shouldCloseConnection;
lock (subscriptionLock)
shouldCloseConnection = !subscriptions.Any(r => r.UserSubscription && subscription != r);
{
if (Status == SocketStatus.Closing)
{
log.Write(LogLevel.Trace, $"Socket {SocketId} already closing");
return;
}
shouldCloseConnection = subscriptions.All(r => !r.UserSubscription || r == subscription);
if (shouldCloseConnection)
Status = SocketStatus.Closing;
}
if (shouldCloseConnection)
{
log.Write(LogLevel.Trace, $"Socket {SocketId} closing as there are no more subscriptions");
await CloseAsync().ConfigureAwait(false);
}
lock (subscriptionLock)
subscriptions.Remove(subscription);
}
private void StartProcessingTask()
{
log.Write(LogLevel.Trace, $"Starting {SocketId} process/reconnect task");
_status = SocketStatus.Processing;
_socketProcessReconnectTask = Task.Run(async () =>
{
await _socket.ProcessAsync().ConfigureAwait(false);
_status = SocketStatus.Reconnecting;
await ReconnectAsync().ConfigureAwait(false);
log.Write(LogLevel.Trace, $"Process/reconnect {SocketId} task finished");
});
}
private async Task ReconnectAsync()
{
// Fail all pending requests
@@ -344,7 +361,8 @@ namespace CryptoExchange.Net.Sockets
}
// Successfully reconnected, start processing
StartProcessingTask();
Status = SocketStatus.Connected;
_socketProcessTask = _socket.ProcessAsync();
ReconnectTry = 0;
var time = DisconnectTime;
@@ -414,7 +432,7 @@ namespace CryptoExchange.Net.Sockets
/// </summary>
public void Dispose()
{
_status = SocketStatus.Disposed;
Status = SocketStatus.Disposed;
_socket.Dispose();
}
@@ -487,10 +505,17 @@ namespace CryptoExchange.Net.Sockets
/// Add a subscription to this connection
/// </summary>
/// <param name="subscription"></param>
public void AddSubscription(SocketSubscription subscription)
public bool AddSubscription(SocketSubscription subscription)
{
lock(subscriptionLock)
lock (subscriptionLock)
{
if (Status != SocketStatus.None && Status != SocketStatus.Connected)
return false;
subscriptions.Add(subscription);
log.Write(LogLevel.Trace, $"Socket {SocketId} adding new subscription with id {subscription.Id}, total subscriptions on connection: {subscriptions.Count}");
return true;
}
}
/// <summary>
@@ -633,6 +658,22 @@ namespace CryptoExchange.Net.Sockets
PausedActivity = false;
}
private async Task ReconnectWatcherAsync()
{
while (true)
{
await _reconnectWaitEvent.WaitAsync().ConfigureAwait(false);
if (!ShouldReconnect)
return;
Status = SocketStatus.Reconnecting;
await ReconnectAsync().ConfigureAwait(false);
if (!ShouldReconnect)
return;
}
}
private async Task<CallResult<bool>> ProcessReconnectAsync()
{
if (!_socket.IsOpen)
@@ -691,11 +732,34 @@ namespace CryptoExchange.Net.Sockets
return await socketClient.SubscribeAndWaitAsync(this, socketSubscription.Request!, socketSubscription).ConfigureAwait(false);
}
private enum SocketStatus
/// <summary>
/// Status of the socket connection
/// </summary>
public enum SocketStatus
{
/// <summary>
/// None/Initial
/// </summary>
None,
Processing,
/// <summary>
/// Connected
/// </summary>
Connected,
/// <summary>
/// Reconnecting
/// </summary>
Reconnecting,
/// <summary>
/// Closing
/// </summary>
Closing,
/// <summary>
/// Closed
/// </summary>
Closed,
/// <summary>
/// Disposed
/// </summary>
Disposed
}
}
+13
View File
@@ -18,6 +18,19 @@ I develop and maintain this package on my own for free in my spare time. Donatio
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf)
## Release notes
* Version 5.1.12 - 12 Jun 2022
* Changed time sync so requests no longer wait for it to complete unless it's the first time
* Made log client options changable after client creation
* Fixed proxy setting not used when reconnecting socket
* Changed MaxSocketConnections to a client options
* Updated socket reconnection logic
* Version 5.1.12 - 12 Jun 2022
* Changed time sync so requests no longer wait for it to complete unless it's the first time
* Made log client options changable after client creation
* Fixed proxy setting not used when reconnecting socket
* Updated socket reconnection logic
* Version 5.1.11 - 24 May 2022
* Added KeepAliveInterval setting
* Fixed port not being copied when setting parameters on request