mirror of
				https://github.com/JKorf/CryptoExchange.Net
				synced 2025-10-31 10:27:48 +00:00 
			
		
		
		
	Finished up websocket refactoring
This commit is contained in:
		
							parent
							
								
									89b517c936
								
							
						
					
					
						commit
						70f8bd203a
					
				| @ -3,6 +3,7 @@ using System.Collections.Concurrent; | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||||
| using System.Linq; | using System.Linq; | ||||||
|  | using System.Text; | ||||||
| using System.Threading; | using System.Threading; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using CryptoExchange.Net.Authentication; | using CryptoExchange.Net.Authentication; | ||||||
| @ -191,10 +192,14 @@ namespace CryptoExchange.Net | |||||||
|                 while (true) |                 while (true) | ||||||
|                 { |                 { | ||||||
|                     // Get a new or existing socket connection |                     // Get a new or existing socket connection | ||||||
|                     socketConnection = GetSocketConnection(apiClient, url, authenticated); |                     var socketResult = await GetSocketConnection(apiClient, url, authenticated).ConfigureAwait(false); | ||||||
|  |                     if(!socketResult) | ||||||
|  |                         return socketResult.As<UpdateSubscription>(null);                     | ||||||
|  | 
 | ||||||
|  |                     socketConnection = socketResult.Data; | ||||||
| 
 | 
 | ||||||
|                     // Add a subscription on the socket connection |                     // Add a subscription on the socket connection | ||||||
|                     subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler); |                     subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler, authenticated); | ||||||
|                     if (subscription == null) |                     if (subscription == null) | ||||||
|                     { |                     { | ||||||
|                         log.Write(LogLevel.Trace, $"Socket {socketConnection.SocketId} failed to add subscription, retrying on different connection"); |                         log.Write(LogLevel.Trace, $"Socket {socketConnection.SocketId} failed to add subscription, retrying on different connection"); | ||||||
| @ -235,7 +240,7 @@ namespace CryptoExchange.Net | |||||||
|                 var subResult = await SubscribeAndWaitAsync(socketConnection, request, subscription).ConfigureAwait(false); |                 var subResult = await SubscribeAndWaitAsync(socketConnection, request, subscription).ConfigureAwait(false); | ||||||
|                 if (!subResult) |                 if (!subResult) | ||||||
|                 { |                 { | ||||||
|                     log.Write(LogLevel.Information, $"Socket {socketConnection.SocketId} failed to subscribe: {subResult.Error}"); |                     log.Write(LogLevel.Warning, $"Socket {socketConnection.SocketId} failed to subscribe: {subResult.Error}"); | ||||||
|                     await socketConnection.CloseAsync(subscription).ConfigureAwait(false); |                     await socketConnection.CloseAsync(subscription).ConfigureAwait(false); | ||||||
|                     return new CallResult<UpdateSubscription>(subResult.Error!); |                     return new CallResult<UpdateSubscription>(subResult.Error!); | ||||||
|                 } |                 } | ||||||
| @ -315,7 +320,12 @@ namespace CryptoExchange.Net | |||||||
|             await semaphoreSlim.WaitAsync().ConfigureAwait(false); |             await semaphoreSlim.WaitAsync().ConfigureAwait(false); | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 socketConnection = GetSocketConnection(apiClient, url, authenticated); |                 var socketResult = await GetSocketConnection(apiClient, url, authenticated).ConfigureAwait(false); | ||||||
|  |                 if (!socketResult) | ||||||
|  |                     return socketResult.As<T>(default); | ||||||
|  | 
 | ||||||
|  |                 socketConnection = socketResult.Data; | ||||||
|  | 
 | ||||||
|                 if (ClientOptions.SocketSubscriptionsCombineTarget == 1) |                 if (ClientOptions.SocketSubscriptionsCombineTarget == 1) | ||||||
|                 { |                 { | ||||||
|                     // Can release early when only a single sub per connection |                     // Can release early when only a single sub per connection | ||||||
| @ -474,8 +484,9 @@ namespace CryptoExchange.Net | |||||||
|         /// <param name="userSubscription">Whether or not this is a user subscription (counts towards the max amount of handlers on a socket)</param> |         /// <param name="userSubscription">Whether or not this is a user subscription (counts towards the max amount of handlers on a socket)</param> | ||||||
|         /// <param name="connection">The socket connection the handler is on</param> |         /// <param name="connection">The socket connection the handler is on</param> | ||||||
|         /// <param name="dataHandler">The handler of the data received</param> |         /// <param name="dataHandler">The handler of the data received</param> | ||||||
|  |         /// <param name="authenticated">Whether the subscription needs authentication</param> | ||||||
|         /// <returns></returns> |         /// <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, bool authenticated) | ||||||
|         { |         { | ||||||
|             void InternalHandler(MessageEvent messageEvent) |             void InternalHandler(MessageEvent messageEvent) | ||||||
|             { |             { | ||||||
| @ -497,8 +508,8 @@ namespace CryptoExchange.Net | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             var subscription = request == null |             var subscription = request == null | ||||||
|                 ? SocketSubscription.CreateForIdentifier(NextId(), identifier!, userSubscription, InternalHandler) |                 ? SocketSubscription.CreateForIdentifier(NextId(), identifier!, userSubscription, authenticated, InternalHandler) | ||||||
|                 : SocketSubscription.CreateForRequest(NextId(), request, userSubscription, InternalHandler); |                 : SocketSubscription.CreateForRequest(NextId(), request, userSubscription, authenticated, InternalHandler); | ||||||
|             if (!connection.AddSubscription(subscription)) |             if (!connection.AddSubscription(subscription)) | ||||||
|                 return null; |                 return null; | ||||||
|             return subscription; |             return subscription; | ||||||
| @ -512,11 +523,23 @@ namespace CryptoExchange.Net | |||||||
|         protected void AddGenericHandler(string identifier, Action<MessageEvent> action) |         protected void AddGenericHandler(string identifier, Action<MessageEvent> action) | ||||||
|         { |         { | ||||||
|             genericHandlers.Add(identifier, action); |             genericHandlers.Add(identifier, action); | ||||||
|             var subscription = SocketSubscription.CreateForIdentifier(NextId(), identifier, false, action); |             var subscription = SocketSubscription.CreateForIdentifier(NextId(), identifier, false, false, action); | ||||||
|             foreach (var connection in socketConnections.Values) |             foreach (var connection in socketConnections.Values) | ||||||
|                 connection.AddSubscription(subscription); |                 connection.AddSubscription(subscription); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Get the url to connect to (defaults to BaseAddress form the client options) | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="apiClient"></param> | ||||||
|  |         /// <param name="address"></param> | ||||||
|  |         /// <param name="authentication"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         protected virtual Task<CallResult<string?>> GetConnectionUrlAsync(SocketApiClient apiClient, string address, bool authentication) | ||||||
|  |         { | ||||||
|  |             return Task.FromResult(new CallResult<string?>(address)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Gets a connection for a new subscription or query. Can be an existing if there are open position or a new one. |         /// Gets a connection for a new subscription or query. Can be an existing if there are open position or a new one. | ||||||
|         /// </summary> |         /// </summary> | ||||||
| @ -524,10 +547,10 @@ namespace CryptoExchange.Net | |||||||
|         /// <param name="address">The address the socket is for</param> |         /// <param name="address">The address the socket is for</param> | ||||||
|         /// <param name="authenticated">Whether the socket should be authenticated</param> |         /// <param name="authenticated">Whether the socket should be authenticated</param> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         protected virtual SocketConnection GetSocketConnection(SocketApiClient apiClient, string address, bool authenticated) |         protected virtual async Task<CallResult<SocketConnection>> GetSocketConnection(SocketApiClient apiClient, string address, bool authenticated) | ||||||
|         { |         { | ||||||
|             var socketResult = socketConnections.Where(s => (s.Value.Status == SocketConnection.SocketStatus.None || s.Value.Status == SocketConnection.SocketStatus.Connected) |             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.Tag.TrimEnd('/') == address.TrimEnd('/') | ||||||
|                                                   && (s.Value.ApiClient.GetType() == apiClient.GetType()) |                                                   && (s.Value.ApiClient.GetType() == apiClient.GetType()) | ||||||
|                                                   && (s.Value.Authenticated == authenticated || !authenticated) && s.Value.Connected).OrderBy(s => s.Value.SubscriptionCount).FirstOrDefault(); |                                                   && (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; |             var result = socketResult.Equals(default(KeyValuePair<int, SocketConnection>)) ? null : socketResult.Value; | ||||||
| @ -536,21 +559,31 @@ namespace CryptoExchange.Net | |||||||
|                 if (result.SubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget || (socketConnections.Count >= ClientOptions.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 |                     // Use existing socket if it has less than target connections OR it has the least connections and we can't make new | ||||||
|                     return result; |                     return new CallResult<SocketConnection>(result); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             var connectionAddress = await GetConnectionUrlAsync(apiClient, address, authenticated).ConfigureAwait(false); | ||||||
|  |             if (!connectionAddress) | ||||||
|  |             { | ||||||
|  |                 log.Write(LogLevel.Warning, $"Failed to determine connection url: " + connectionAddress.Error); | ||||||
|  |                 return connectionAddress.As<SocketConnection>(null); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (connectionAddress.Data != address) | ||||||
|  |                 log.Write(LogLevel.Debug, $"Connection address set to " + connectionAddress.Data); | ||||||
|  | 
 | ||||||
|             // Create new socket |             // Create new socket | ||||||
|             var socket = CreateSocket(address); |             var socket = CreateSocket(connectionAddress.Data!); | ||||||
|             var socketConnection = new SocketConnection(this, apiClient, socket); |             var socketConnection = new SocketConnection(this, apiClient, socket, address); | ||||||
|             socketConnection.UnhandledMessage += HandleUnhandledMessage; |             socketConnection.UnhandledMessage += HandleUnhandledMessage; | ||||||
|             foreach (var kvp in genericHandlers) |             foreach (var kvp in genericHandlers) | ||||||
|             { |             { | ||||||
|                 var handler = SocketSubscription.CreateForIdentifier(NextId(), kvp.Key, false, kvp.Value); |                 var handler = SocketSubscription.CreateForIdentifier(NextId(), kvp.Key, false, false, kvp.Value); | ||||||
|                 socketConnection.AddSubscription(handler); |                 socketConnection.AddSubscription(handler); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return socketConnection; |             return new CallResult<SocketConnection>(socketConnection); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @ -700,7 +733,7 @@ namespace CryptoExchange.Net | |||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public virtual async Task UnsubscribeAllAsync() |         public virtual async Task UnsubscribeAllAsync() | ||||||
|         { |         { | ||||||
|             log.Write(LogLevel.Information, $"Closing all {socketConnections.Sum(s => s.Value.SubscriptionCount)} subscriptions"); |             log.Write(LogLevel.Information, $"Unsubscribing all {socketConnections.Sum(s => s.Value.SubscriptionCount)} subscriptions"); | ||||||
|             var tasks = new List<Task>(); |             var tasks = new List<Task>(); | ||||||
|             { |             { | ||||||
|                 var socketList = socketConnections.Values; |                 var socketList = socketConnections.Values; | ||||||
| @ -711,6 +744,39 @@ namespace CryptoExchange.Net | |||||||
|             await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false); |             await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Reconnect all connections | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public virtual async Task ReconnectAsync() | ||||||
|  |         { | ||||||
|  |             log.Write(LogLevel.Information, $"Reconnecting all {socketConnections.Count} connections"); | ||||||
|  |             var tasks = new List<Task>(); | ||||||
|  |             { | ||||||
|  |                 var socketList = socketConnections.Values; | ||||||
|  |                 foreach (var sub in socketList) | ||||||
|  |                     tasks.Add(sub.TriggerReconnectAsync()); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Log the current state of connections and subscriptions | ||||||
|  |         /// </summary> | ||||||
|  |         public string GetSubscriptionsState() | ||||||
|  |         { | ||||||
|  |             var sb = new StringBuilder(); | ||||||
|  |             sb.AppendLine($"{socketConnections.Count} connections, {CurrentSubscriptions} subscriptions, kbps: {IncomingKbps}"); | ||||||
|  |             foreach(var connection in socketConnections) | ||||||
|  |             { | ||||||
|  |                 sb.AppendLine($"  Connection {connection.Key}: {connection.Value.SubscriptionCount} subscriptions, status: {connection.Value.Status}, authenticated: {connection.Value.Authenticated}, kbps: {connection.Value.IncomingKbps}"); | ||||||
|  |                 foreach (var subscription in connection.Value.Subscriptions) | ||||||
|  |                     sb.AppendLine($"    Subscription {subscription.Id}, authenticated: {subscription.Authenticated}, confirmed: {subscription.Confirmed}"); | ||||||
|  |             } | ||||||
|  |             return sb.ToString(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Dispose the client |         /// Dispose the client | ||||||
|         /// </summary> |         /// </summary> | ||||||
| @ -719,8 +785,11 @@ namespace CryptoExchange.Net | |||||||
|             disposing = true; |             disposing = true; | ||||||
|             periodicEvent?.Set(); |             periodicEvent?.Set(); | ||||||
|             periodicEvent?.Dispose(); |             periodicEvent?.Dispose(); | ||||||
|  |             if (socketConnections.Sum(s => s.Value.SubscriptionCount) > 0) | ||||||
|  |             { | ||||||
|                 log.Write(LogLevel.Debug, "Disposing socket client, closing all subscriptions"); |                 log.Write(LogLevel.Debug, "Disposing socket client, closing all subscriptions"); | ||||||
|                 _ = UnsubscribeAllAsync(); |                 _ = UnsubscribeAllAsync(); | ||||||
|  |             } | ||||||
|             semaphoreSlim?.Dispose(); |             semaphoreSlim?.Dispose(); | ||||||
|             base.Dispose(); |             base.Dispose(); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -27,13 +27,6 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             Reconnecting |             Reconnecting | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         enum CloseState |  | ||||||
|         { |  | ||||||
|             Idle, |  | ||||||
|             Closing, |  | ||||||
|             Closed |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         internal static int lastStreamId; |         internal static int lastStreamId; | ||||||
|         private static readonly object streamIdLock = new(); |         private static readonly object streamIdLock = new(); | ||||||
| 
 | 
 | ||||||
| @ -191,13 +184,13 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         { |         { | ||||||
|             while (!_stopRequested) |             while (!_stopRequested) | ||||||
|             { |             { | ||||||
|                 _log.Write(LogLevel.Trace, $"Socket {Id} ProcessAsync started"); |                 _log.Write(LogLevel.Debug, $"Socket {Id} starting processing tasks"); | ||||||
|                 _processState = ProcessState.Processing; |                 _processState = ProcessState.Processing; | ||||||
|                 var sendTask = SendLoopAsync(); |                 var sendTask = SendLoopAsync(); | ||||||
|                 var receiveTask = ReceiveLoopAsync(); |                 var receiveTask = ReceiveLoopAsync(); | ||||||
|                 var timeoutTask = _parameters.Timeout != null && _parameters.Timeout > TimeSpan.FromSeconds(0) ? CheckTimeoutAsync() : Task.CompletedTask; |                 var timeoutTask = _parameters.Timeout != null && _parameters.Timeout > TimeSpan.FromSeconds(0) ? CheckTimeoutAsync() : Task.CompletedTask; | ||||||
|                 await Task.WhenAll(sendTask, receiveTask, timeoutTask).ConfigureAwait(false); |                 await Task.WhenAll(sendTask, receiveTask, timeoutTask).ConfigureAwait(false); | ||||||
|                 _log.Write(LogLevel.Trace, $"Socket {Id} ProcessAsync finished"); |                 _log.Write(LogLevel.Debug, $"Socket {Id} processing tasks finished"); | ||||||
| 
 | 
 | ||||||
|                 _processState = ProcessState.WaitingForClose; |                 _processState = ProcessState.WaitingForClose; | ||||||
|                 while (_closeTask == null) |                 while (_closeTask == null) | ||||||
| @ -221,7 +214,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
| 
 | 
 | ||||||
|                 while (!_stopRequested) |                 while (!_stopRequested) | ||||||
|                 { |                 { | ||||||
|                     _log.Write(LogLevel.Trace, $"Socket {Id} attempting to reconnect"); |                     _log.Write(LogLevel.Debug, $"Socket {Id} attempting to reconnect"); | ||||||
|                     _socket = CreateSocket(); |                     _socket = CreateSocket(); | ||||||
|                     _ctsSource.Dispose(); |                     _ctsSource.Dispose(); | ||||||
|                     _ctsSource = new CancellationTokenSource(); |                     _ctsSource = new CancellationTokenSource(); | ||||||
| @ -260,7 +253,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             if (_processState != ProcessState.Processing) |             if (_processState != ProcessState.Processing) | ||||||
|                 return; |                 return; | ||||||
| 
 | 
 | ||||||
|             _log.Write(LogLevel.Debug, $"Socket {Id} reconnecting"); |             _log.Write(LogLevel.Debug, $"Socket {Id} reconnect requested"); | ||||||
|             _closeTask = CloseInternalAsync(); |             _closeTask = CloseInternalAsync(); | ||||||
|             await _closeTask.ConfigureAwait(false); |             await _closeTask.ConfigureAwait(false); | ||||||
|         } |         } | ||||||
| @ -318,7 +311,6 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     _log.Write(LogLevel.Trace, $"Socket {Id} normal closure 1"); |  | ||||||
|                     await _socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", default).ConfigureAwait(false); |                     await _socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", default).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch (Exception) |                 catch (Exception) | ||||||
| @ -332,7 +324,6 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     _log.Write(LogLevel.Trace, $"Socket {Id} normal closure 2"); |  | ||||||
|                     await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", default).ConfigureAwait(false); |                     await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", default).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch (Exception) |                 catch (Exception) | ||||||
| @ -390,7 +381,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|                             } |                             } | ||||||
| 
 | 
 | ||||||
|                             if (start != null) |                             if (start != null) | ||||||
|                                 _log.Write(LogLevel.Trace, $"Socket {Id} sent delayed {Math.Round((DateTime.UtcNow - start.Value).TotalMilliseconds)}ms because of rate limit"); |                                 _log.Write(LogLevel.Debug, $"Socket {Id} sent delayed {Math.Round((DateTime.UtcNow - start.Value).TotalMilliseconds)}ms because of rate limit"); | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         try |                         try | ||||||
| @ -424,7 +415,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             } |             } | ||||||
|             finally |             finally | ||||||
|             { |             { | ||||||
|                 _log.Write(LogLevel.Trace, $"Socket {Id} Send loop finished"); |                 _log.Write(LogLevel.Debug, $"Socket {Id} Send loop finished"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -542,7 +533,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             } |             } | ||||||
|             finally |             finally | ||||||
|             { |             { | ||||||
|                 _log.Write(LogLevel.Trace, $"Socket {Id} Receive loop finished"); |                 _log.Write(LogLevel.Debug, $"Socket {Id} Receive loop finished"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -57,10 +57,22 @@ namespace CryptoExchange.Net.Sockets | |||||||
|                 return subscriptions.Count(h => h.UserSubscription); } |                 return subscriptions.Count(h => h.UserSubscription); } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Get a copy of the current subscriptions | ||||||
|  |         /// </summary> | ||||||
|  |         public SocketSubscription[] Subscriptions | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 lock (subscriptionLock) | ||||||
|  |                     return subscriptions.Where(h => h.UserSubscription).ToArray(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// If the connection has been authenticated |         /// If the connection has been authenticated | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public bool Authenticated { get; set; } |         public bool Authenticated { get; internal set; } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// If connection is made |         /// If connection is made | ||||||
| @ -80,7 +92,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// The connection uri |         /// The connection uri | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public Uri Uri => _socket.Uri; |         public Uri ConnectionUri => _socket.Uri; | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// The API client the connection is for |         /// The API client the connection is for | ||||||
| @ -95,7 +107,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Tag for identificaion |         /// Tag for identificaion | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public string? Tag { get; set; } |         public string Tag { get; set; } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// If activity is paused |         /// If activity is paused | ||||||
| @ -128,7 +140,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
| 
 | 
 | ||||||
|                 var oldStatus = _status; |                 var oldStatus = _status; | ||||||
|                 _status = value; |                 _status = value; | ||||||
|                 log.Write(LogLevel.Trace, $"Socket {SocketId} status changed from {oldStatus} to {_status}"); |                 log.Write(LogLevel.Debug, $"Socket {SocketId} status changed from {oldStatus} to {_status}"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -154,11 +166,12 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         /// <param name="client">The socket client</param> |         /// <param name="client">The socket client</param> | ||||||
|         /// <param name="apiClient">The api client</param> |         /// <param name="apiClient">The api client</param> | ||||||
|         /// <param name="socket">The socket</param> |         /// <param name="socket">The socket</param> | ||||||
|         public SocketConnection(BaseSocketClient client, SocketApiClient apiClient, IWebsocket socket) |         public SocketConnection(BaseSocketClient client, SocketApiClient apiClient, IWebsocket socket, string tag) | ||||||
|         { |         { | ||||||
|             log = client.log; |             log = client.log; | ||||||
|             socketClient = client; |             socketClient = client; | ||||||
|             ApiClient = apiClient; |             ApiClient = apiClient; | ||||||
|  |             Tag = tag; | ||||||
| 
 | 
 | ||||||
|             pendingRequests = new List<PendingRequest>(); |             pendingRequests = new List<PendingRequest>(); | ||||||
|             subscriptions = new List<SocketSubscription>(); |             subscriptions = new List<SocketSubscription>(); | ||||||
| @ -187,6 +200,12 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         protected virtual void HandleClose() |         protected virtual void HandleClose() | ||||||
|         { |         { | ||||||
|             Status = SocketStatus.Closed; |             Status = SocketStatus.Closed; | ||||||
|  |             Authenticated = false; | ||||||
|  |             lock(subscriptionLock) | ||||||
|  |             { | ||||||
|  |                 foreach (var sub in subscriptions) | ||||||
|  |                     sub.Confirmed = false; | ||||||
|  |             }     | ||||||
|             Task.Run(() => ConnectionClosed?.Invoke()); |             Task.Run(() => ConnectionClosed?.Invoke()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -197,6 +216,12 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         { |         { | ||||||
|             Status = SocketStatus.Reconnecting; |             Status = SocketStatus.Reconnecting; | ||||||
|             DisconnectTime = DateTime.UtcNow; |             DisconnectTime = DateTime.UtcNow; | ||||||
|  |             Authenticated = false; | ||||||
|  |             lock (subscriptionLock) | ||||||
|  |             { | ||||||
|  |                 foreach (var sub in subscriptions) | ||||||
|  |                     sub.Confirmed = false; | ||||||
|  |             } | ||||||
|             Task.Run(() => ConnectionLost?.Invoke()); |             Task.Run(() => ConnectionLost?.Invoke()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -365,7 +390,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             if (Status == SocketStatus.Closing || Status == SocketStatus.Closed || Status == SocketStatus.Disposed) |             if (Status == SocketStatus.Closing || Status == SocketStatus.Closed || Status == SocketStatus.Disposed) | ||||||
|                 return; |                 return; | ||||||
| 
 | 
 | ||||||
|             log.Write(LogLevel.Trace, $"Socket {SocketId} closing subscription {subscription.Id}"); |             log.Write(LogLevel.Debug, $"Socket {SocketId} closing subscription {subscription.Id}"); | ||||||
|             if (subscription.CancellationTokenRegistration.HasValue) |             if (subscription.CancellationTokenRegistration.HasValue) | ||||||
|                 subscription.CancellationTokenRegistration.Value.Dispose(); |                 subscription.CancellationTokenRegistration.Value.Dispose(); | ||||||
| 
 | 
 | ||||||
| @ -377,7 +402,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|             { |             { | ||||||
|                 if (Status == SocketStatus.Closing) |                 if (Status == SocketStatus.Closing) | ||||||
|                 { |                 { | ||||||
|                     log.Write(LogLevel.Trace, $"Socket {SocketId} already closing"); |                     log.Write(LogLevel.Debug, $"Socket {SocketId} already closing"); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
| @ -388,7 +413,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
| 
 | 
 | ||||||
|             if (shouldCloseConnection) |             if (shouldCloseConnection) | ||||||
|             { |             { | ||||||
|                 log.Write(LogLevel.Trace, $"Socket {SocketId} closing as there are no more subscriptions"); |                 log.Write(LogLevel.Debug, $"Socket {SocketId} closing as there are no more subscriptions"); | ||||||
|                 await CloseAsync().ConfigureAwait(false); |                 await CloseAsync().ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -414,7 +439,8 @@ namespace CryptoExchange.Net.Sockets | |||||||
|                     return false; |                     return false; | ||||||
| 
 | 
 | ||||||
|                 subscriptions.Add(subscription); |                 subscriptions.Add(subscription); | ||||||
|                 log.Write(LogLevel.Trace, $"Socket {SocketId} adding new subscription with id {subscription.Id}, total subscriptions on connection: {subscriptions.Count}"); |                 if(subscription.UserSubscription) | ||||||
|  |                     log.Write(LogLevel.Debug, $"Socket {SocketId} adding new subscription with id {subscription.Id}, total subscriptions on connection: {subscriptions.Count(s => s.UserSubscription)}"); | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -567,7 +593,7 @@ namespace CryptoExchange.Net.Sockets | |||||||
|                 return new CallResult<bool>(true); |                 return new CallResult<bool>(true); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (Authenticated) |             if (subscriptions.Any(s => s.Authenticated)) | ||||||
|             { |             { | ||||||
|                 // If we reconnected a authenticated connection we need to re-authenticate |                 // If we reconnected a authenticated connection we need to re-authenticate | ||||||
|                 var authResult = await socketClient.AuthenticateSocketAsync(this).ConfigureAwait(false); |                 var authResult = await socketClient.AuthenticateSocketAsync(this).ConfigureAwait(false); | ||||||
| @ -577,13 +603,22 @@ namespace CryptoExchange.Net.Sockets | |||||||
|                     return authResult; |                     return authResult; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  |                 Authenticated = true; | ||||||
|                 log.Write(LogLevel.Debug, $"Socket {SocketId} authentication succeeded on reconnected socket."); |                 log.Write(LogLevel.Debug, $"Socket {SocketId} authentication succeeded on reconnected socket."); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Get a list of all subscriptions on the socket |             // Get a list of all subscriptions on the socket | ||||||
|             List<SocketSubscription> subscriptionList; |             List<SocketSubscription> subscriptionList = new List<SocketSubscription>(); | ||||||
|             lock (subscriptionLock) |             lock (subscriptionLock) | ||||||
|                 subscriptionList = subscriptions.Where(h => h.Request != null).ToList(); |             { | ||||||
|  |                 foreach (var subscription in subscriptions) | ||||||
|  |                 { | ||||||
|  |                     if (subscription.Request != null) | ||||||
|  |                         subscriptionList.Add(subscription); | ||||||
|  |                     else | ||||||
|  |                         subscription.Confirmed = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             // Foreach subscription which is subscribed by a subscription request we will need to resend that request to resubscribe |             // Foreach subscription which is subscribed by a subscription request we will need to resend that request to resubscribe | ||||||
|             for (var i = 0; i < subscriptionList.Count; i += socketClient.ClientOptions.MaxConcurrentResubscriptionsPerSocket) |             for (var i = 0; i < subscriptionList.Count; i += socketClient.ClientOptions.MaxConcurrentResubscriptionsPerSocket) | ||||||
| @ -600,6 +635,9 @@ namespace CryptoExchange.Net.Sockets | |||||||
|                     return taskList.First(t => !t.Result.Success).Result; |                     return taskList.First(t => !t.Result.Success).Result; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             foreach (var subscription in subscriptionList) | ||||||
|  |                 subscription.Confirmed = true; | ||||||
|  | 
 | ||||||
|             if (!_socket.IsOpen) |             if (!_socket.IsOpen) | ||||||
|                 return new CallResult<bool>(new WebError("Socket not connected")); |                 return new CallResult<bool>(new WebError("Socket not connected")); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -43,19 +43,25 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         /// </summary> |         /// </summary> | ||||||
|         public bool Confirmed { get; set; } |         public bool Confirmed { get; set; } | ||||||
| 
 | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Whether authentication is needed for this subscription | ||||||
|  |         /// </summary> | ||||||
|  |         public bool Authenticated { get; set; } | ||||||
|  | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Cancellation token registration, should be disposed when subscription is closed. Used for closing the subscription with  |         /// Cancellation token registration, should be disposed when subscription is closed. Used for closing the subscription with  | ||||||
|         /// a provided cancelation token |         /// a provided cancelation token | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public CancellationTokenRegistration? CancellationTokenRegistration { get; set; } |         public CancellationTokenRegistration? CancellationTokenRegistration { get; set; } | ||||||
| 
 | 
 | ||||||
|         private SocketSubscription(int id, object? request, string? identifier, bool userSubscription, Action<MessageEvent> dataHandler) |         private SocketSubscription(int id, object? request, string? identifier, bool userSubscription, bool authenticated, Action<MessageEvent> dataHandler) | ||||||
|         { |         { | ||||||
|             Id = id; |             Id = id; | ||||||
|             UserSubscription = userSubscription; |             UserSubscription = userSubscription; | ||||||
|             MessageHandler = dataHandler; |             MessageHandler = dataHandler; | ||||||
|             Request = request; |             Request = request; | ||||||
|             Identifier = identifier; |             Identifier = identifier; | ||||||
|  |             Authenticated = authenticated; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @ -67,9 +73,9 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         /// <param name="dataHandler"></param> |         /// <param name="dataHandler"></param> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public static SocketSubscription CreateForRequest(int id, object request, bool userSubscription, |         public static SocketSubscription CreateForRequest(int id, object request, bool userSubscription, | ||||||
|             Action<MessageEvent> dataHandler) |             bool authenticated, Action<MessageEvent> dataHandler) | ||||||
|         { |         { | ||||||
|             return new SocketSubscription(id, request, null, userSubscription, dataHandler); |             return new SocketSubscription(id, request, null, userSubscription, authenticated, dataHandler); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @ -81,9 +87,9 @@ namespace CryptoExchange.Net.Sockets | |||||||
|         /// <param name="dataHandler"></param> |         /// <param name="dataHandler"></param> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public static SocketSubscription CreateForIdentifier(int id, string identifier, bool userSubscription, |         public static SocketSubscription CreateForIdentifier(int id, string identifier, bool userSubscription, | ||||||
|             Action<MessageEvent> dataHandler) |             bool authenticated, Action<MessageEvent> dataHandler) | ||||||
|         { |         { | ||||||
|             return new SocketSubscription(id, null, identifier, userSubscription, dataHandler); |             return new SocketSubscription(id, null, identifier, userSubscription, authenticated, dataHandler); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user