mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-08 08:26:20 +00:00
added incoming kbps property for sockets, updated logging
This commit is contained in:
parent
89de0da724
commit
aa07029c21
@ -1353,6 +1353,11 @@
|
|||||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.MaxConcurrentResubscriptionsPerSocket">
|
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.MaxConcurrentResubscriptionsPerSocket">
|
||||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.IncomingKbps">
|
||||||
|
<summary>
|
||||||
|
The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Interfaces.ISocketClient.UnsubscribeAsync(CryptoExchange.Net.Sockets.UpdateSubscription)">
|
<member name="M:CryptoExchange.Net.Interfaces.ISocketClient.UnsubscribeAsync(CryptoExchange.Net.Sockets.UpdateSubscription)">
|
||||||
<summary>
|
<summary>
|
||||||
Unsubscribe from a stream
|
Unsubscribe from a stream
|
||||||
@ -1548,6 +1553,11 @@
|
|||||||
The max amount of outgoing messages per second
|
The max amount of outgoing messages per second
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.Interfaces.IWebsocket.IncomingKbps">
|
||||||
|
<summary>
|
||||||
|
The current kilobytes per second of data being received, averaged over the last 3 seconds
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="P:CryptoExchange.Net.Interfaces.IWebsocket.DataInterpreterBytes">
|
<member name="P:CryptoExchange.Net.Interfaces.IWebsocket.DataInterpreterBytes">
|
||||||
<summary>
|
<summary>
|
||||||
Handler for byte data
|
Handler for byte data
|
||||||
@ -1843,6 +1853,14 @@
|
|||||||
<param name="error"></param>
|
<param name="error"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:CryptoExchange.Net.Objects.CallResult`1.As``1(``0)">
|
||||||
|
<summary>
|
||||||
|
Copy the WebCallResult to a new data type
|
||||||
|
</summary>
|
||||||
|
<typeparam name="K">The new type</typeparam>
|
||||||
|
<param name="data">The data of the new type</param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="T:CryptoExchange.Net.Objects.WebCallResult">
|
<member name="T:CryptoExchange.Net.Objects.WebCallResult">
|
||||||
<summary>
|
<summary>
|
||||||
The result of a request
|
The result of a request
|
||||||
@ -3129,6 +3147,11 @@
|
|||||||
The max amount of outgoing messages per socket per second
|
The max amount of outgoing messages per socket per second
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.SocketClient.IncomingKbps">
|
||||||
|
<summary>
|
||||||
|
The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.SocketClient.#ctor(System.String,CryptoExchange.Net.Objects.SocketClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
<member name="M:CryptoExchange.Net.SocketClient.#ctor(System.String,CryptoExchange.Net.Objects.SocketClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
||||||
<summary>
|
<summary>
|
||||||
ctor
|
ctor
|
||||||
@ -3454,6 +3477,11 @@
|
|||||||
The timespan no data is received on the socket. If no data is received within this time an error is generated
|
The timespan no data is received on the socket. If no data is received within this time an error is generated
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:CryptoExchange.Net.Sockets.CryptoExchangeWebSocketClient.IncomingKbps">
|
||||||
|
<summary>
|
||||||
|
The current kilobytes per second of data being received, averaged over the last 3 seconds
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="E:CryptoExchange.Net.Sockets.CryptoExchangeWebSocketClient.OnClose">
|
<member name="E:CryptoExchange.Net.Sockets.CryptoExchangeWebSocketClient.OnClose">
|
||||||
<summary>
|
<summary>
|
||||||
Socket closed event
|
Socket closed event
|
||||||
@ -3969,148 +3997,5 @@
|
|||||||
<member name="M:CryptoExchange.Net.Sockets.WebsocketFactory.CreateWebsocket(CryptoExchange.Net.Logging.Log,System.String,System.Collections.Generic.IDictionary{System.String,System.String},System.Collections.Generic.IDictionary{System.String,System.String})">
|
<member name="M:CryptoExchange.Net.Sockets.WebsocketFactory.CreateWebsocket(CryptoExchange.Net.Logging.Log,System.String,System.Collections.Generic.IDictionary{System.String,System.String},System.Collections.Generic.IDictionary{System.String,System.String})">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
</member>
|
</member>
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that <see langword="null"/> is allowed as an input even if the
|
|
||||||
corresponding type disallows it.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.AllowNullAttribute.#ctor">
|
|
||||||
<summary>
|
|
||||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute"/> class.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that <see langword="null"/> is disallowed as an input even if the
|
|
||||||
corresponding type allows it.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.DisallowNullAttribute.#ctor">
|
|
||||||
<summary>
|
|
||||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute"/> class.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that a method that will never return under any circumstance.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.#ctor">
|
|
||||||
<summary>
|
|
||||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute"/> class.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that the method will not return if the associated <see cref="T:System.Boolean"/>
|
|
||||||
parameter is passed the specified value.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.ParameterValue">
|
|
||||||
<summary>
|
|
||||||
Gets the condition parameter value.
|
|
||||||
Code after the method is considered unreachable by diagnostics if the argument
|
|
||||||
to the associated parameter matches this value.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.#ctor(System.Boolean)">
|
|
||||||
<summary>
|
|
||||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute"/>
|
|
||||||
class with the specified parameter value.
|
|
||||||
</summary>
|
|
||||||
<param name="parameterValue">
|
|
||||||
The condition parameter value.
|
|
||||||
Code after the method is considered unreachable by diagnostics if the argument
|
|
||||||
to the associated parameter matches this value.
|
|
||||||
</param>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that an output may be <see langword="null"/> even if the
|
|
||||||
corresponding type disallows it.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullAttribute.#ctor">
|
|
||||||
<summary>
|
|
||||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute"/> class.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue"/>,
|
|
||||||
the parameter may be <see langword="null"/> even if the corresponding type disallows it.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue">
|
|
||||||
<summary>
|
|
||||||
Gets the return value condition.
|
|
||||||
If the method returns this value, the associated parameter may be <see langword="null"/>.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.#ctor(System.Boolean)">
|
|
||||||
<summary>
|
|
||||||
Initializes the attribute with the specified return value condition.
|
|
||||||
</summary>
|
|
||||||
<param name="returnValue">
|
|
||||||
The return value condition.
|
|
||||||
If the method returns this value, the associated parameter may be <see langword="null"/>.
|
|
||||||
</param>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that an output is not <see langword="null"/> even if the
|
|
||||||
corresponding type allows it.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullAttribute.#ctor">
|
|
||||||
<summary>
|
|
||||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.NotNullAttribute"/> class.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that the output will be non-<see langword="null"/> if the
|
|
||||||
named parameter is non-<see langword="null"/>.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.ParameterName">
|
|
||||||
<summary>
|
|
||||||
Gets the associated parameter name.
|
|
||||||
The output will be non-<see langword="null"/> if the argument to the
|
|
||||||
parameter specified is non-<see langword="null"/>.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.#ctor(System.String)">
|
|
||||||
<summary>
|
|
||||||
Initializes the attribute with the associated parameter name.
|
|
||||||
</summary>
|
|
||||||
<param name="parameterName">
|
|
||||||
The associated parameter name.
|
|
||||||
The output will be non-<see langword="null"/> if the argument to the
|
|
||||||
parameter specified is non-<see langword="null"/>.
|
|
||||||
</param>
|
|
||||||
</member>
|
|
||||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute">
|
|
||||||
<summary>
|
|
||||||
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue"/>,
|
|
||||||
the parameter will not be <see langword="null"/> even if the corresponding type allows it.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue">
|
|
||||||
<summary>
|
|
||||||
Gets the return value condition.
|
|
||||||
If the method returns this value, the associated parameter will not be <see langword="null"/>.
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.#ctor(System.Boolean)">
|
|
||||||
<summary>
|
|
||||||
Initializes the attribute with the specified return value condition.
|
|
||||||
</summary>
|
|
||||||
<param name="returnValue">
|
|
||||||
The return value condition.
|
|
||||||
If the method returns this value, the associated parameter will not be <see langword="null"/>.
|
|
||||||
</param>
|
|
||||||
</member>
|
|
||||||
</members>
|
</members>
|
||||||
</doc>
|
</doc>
|
||||||
|
@ -49,6 +49,10 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
int? MaxResubscribeTries { get; }
|
int? MaxResubscribeTries { get; }
|
||||||
/// <inheritdoc cref="SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
/// <inheritdoc cref="SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
||||||
int MaxConcurrentResubscriptionsPerSocket { get; }
|
int MaxConcurrentResubscriptionsPerSocket { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||||
|
/// </summary>
|
||||||
|
double IncomingKbps { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unsubscribe from a stream
|
/// Unsubscribe from a stream
|
||||||
|
@ -47,7 +47,11 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The max amount of outgoing messages per second
|
/// The max amount of outgoing messages per second
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? RatelimitPerSecond { get; set; }
|
int? RatelimitPerSecond { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The current kilobytes per second of data being received, averaged over the last 3 seconds
|
||||||
|
/// </summary>
|
||||||
|
double IncomingKbps { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handler for byte data
|
/// Handler for byte data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -120,6 +120,17 @@ namespace CryptoExchange.Net.Objects
|
|||||||
{
|
{
|
||||||
return new WebCallResult<T>(null, null, default, error);
|
return new WebCallResult<T>(null, null, default, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy the WebCallResult to a new data type
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="K">The new type</typeparam>
|
||||||
|
/// <param name="data">The data of the new type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public CallResult<K> As<K>([AllowNull] K data)
|
||||||
|
{
|
||||||
|
return new CallResult<K>(data, Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -447,7 +447,8 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
|
|
||||||
if (asks.First().Key < bids.First().Key)
|
if (asks.First().Key < bids.First().Key)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} detected out of sync order book. Resyncing");
|
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} detected out of sync order book. First ask: {asks.First().Key}, first bid: {bids.First().Key}. Resyncing");
|
||||||
|
_stopProcessing = true;
|
||||||
Resubscribe();
|
Resubscribe();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -636,6 +637,7 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
{
|
{
|
||||||
// Out of sync
|
// Out of sync
|
||||||
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} out of sync (expected { LastSequenceNumber + 1}, was {sequence}), reconnecting");
|
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} out of sync (expected { LastSequenceNumber + 1}, was {sequence}), reconnecting");
|
||||||
|
_stopProcessing = true;
|
||||||
Resubscribe();
|
Resubscribe();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,20 @@ namespace CryptoExchange.Net
|
|||||||
/// The max amount of outgoing messages per socket per second
|
/// The max amount of outgoing messages per socket per second
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected internal int? RateLimitPerSocketPerSecond { get; set; }
|
protected internal int? RateLimitPerSocketPerSecond { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||||
|
/// </summary>
|
||||||
|
public double IncomingKbps
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!sockets.Any())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return sockets.Sum(s => s.Value.Socket.IncomingKbps);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -193,7 +207,7 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
if (socketConnection.PausedActivity)
|
if (socketConnection.PausedActivity)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Information, "Socket has been paused, can't subscribe at this moment");
|
log.Write(LogLevel.Information, $"Socket {socketConnection.Socket.Id} has been paused, can't subscribe at this moment");
|
||||||
return new CallResult<UpdateSubscription>(default, new ServerError("Socket is paused"));
|
return new CallResult<UpdateSubscription>(default, new ServerError("Socket is paused"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +298,7 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
if (socketConnection.PausedActivity)
|
if (socketConnection.PausedActivity)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Information, "Socket has been paused, can't send query at this moment");
|
log.Write(LogLevel.Information, $"Socket {socketConnection.Socket.Id} has been paused, can't send query at this moment");
|
||||||
return new CallResult<T>(default, new ServerError("Socket is paused"));
|
return new CallResult<T>(default, new ServerError("Socket is paused"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +348,7 @@ namespace CryptoExchange.Net
|
|||||||
var result = await AuthenticateSocketAsync(socket).ConfigureAwait(false);
|
var result = await AuthenticateSocketAsync(socket).ConfigureAwait(false);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Warning, "Socket authentication failed");
|
log.Write(LogLevel.Warning, $"Socket {socket.Socket.Id} authentication failed");
|
||||||
result.Error!.Message = "Authentication failed: " + result.Error.Message;
|
result.Error!.Message = "Authentication failed: " + result.Error.Message;
|
||||||
return new CallResult<bool>(false, result.Error);
|
return new CallResult<bool>(false, result.Error);
|
||||||
}
|
}
|
||||||
@ -435,7 +449,7 @@ namespace CryptoExchange.Net
|
|||||||
var desResult = Deserialize<T>(messageEvent.JsonData, false);
|
var desResult = Deserialize<T>(messageEvent.JsonData, false);
|
||||||
if (!desResult)
|
if (!desResult)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Warning, $"Failed to deserialize data into type {typeof(T)}: {desResult.Error}");
|
log.Write(LogLevel.Warning, $"Socket {connection.Socket.Id} Failed to deserialize data into type {typeof(T)}: {desResult.Error}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,7 +542,7 @@ namespace CryptoExchange.Net
|
|||||||
protected virtual IWebsocket CreateSocket(string address)
|
protected virtual IWebsocket CreateSocket(string address)
|
||||||
{
|
{
|
||||||
var socket = SocketFactory.CreateWebsocket(log, address);
|
var socket = SocketFactory.CreateWebsocket(log, address);
|
||||||
log.Write(LogLevel.Debug, "Created new socket for " + address);
|
log.Write(LogLevel.Debug, $"Socket {socket.Id} new socket created for " + address);
|
||||||
|
|
||||||
if (apiProxy != null)
|
if (apiProxy != null)
|
||||||
socket.SetProxy(apiProxy);
|
socket.SetProxy(apiProxy);
|
||||||
@ -566,9 +580,6 @@ namespace CryptoExchange.Net
|
|||||||
if (disposing)
|
if (disposing)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (sockets.Any())
|
|
||||||
log.Write(LogLevel.Debug, "Sending periodic");
|
|
||||||
|
|
||||||
foreach (var socket in sockets.Values)
|
foreach (var socket in sockets.Values)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
@ -578,13 +589,15 @@ namespace CryptoExchange.Net
|
|||||||
if (obj == null)
|
if (obj == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
log.Write(LogLevel.Trace, $"Socket {socket.Socket.Id} sending periodic");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
socket.Send(obj);
|
socket.Send(obj);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Warning, "Periodic send failed: " + ex);
|
log.Write(LogLevel.Warning, $"Socket {socket.Socket.Id} Periodic send failed: " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.Collections.Concurrent;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
@ -36,7 +37,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
private bool _closing;
|
private bool _closing;
|
||||||
private bool _startedSent;
|
private bool _startedSent;
|
||||||
private bool _startedReceive;
|
private bool _startedReceive;
|
||||||
private readonly List<DateTime> outgoingMessages;
|
|
||||||
|
private readonly List<DateTime> _outgoingMessages;
|
||||||
|
protected readonly Dictionary<DateTime, int> _receivedMessages;
|
||||||
|
private DateTime _lastReceivedMessagesUpdate;
|
||||||
|
protected readonly object _receivedMessagesLock;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Log
|
/// Log
|
||||||
@ -126,6 +131,25 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan Timeout { get; set; }
|
public TimeSpan Timeout { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current kilobytes per second of data being received, averaged over the last 3 seconds
|
||||||
|
/// </summary>
|
||||||
|
public double IncomingKbps
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
UpdateReceivedMessages();
|
||||||
|
|
||||||
|
lock (_receivedMessagesLock)
|
||||||
|
{
|
||||||
|
if (!_receivedMessages.Any())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return Math.Round(_receivedMessages.Values.Sum(v => v) / 1000 / 3d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Socket closed event
|
/// Socket closed event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -183,10 +207,12 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
this.cookies = cookies;
|
this.cookies = cookies;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
|
|
||||||
outgoingMessages = new List<DateTime>();
|
_outgoingMessages = new List<DateTime>();
|
||||||
|
_receivedMessages = new Dictionary<DateTime, int>();
|
||||||
_sendEvent = new AutoResetEvent(false);
|
_sendEvent = new AutoResetEvent(false);
|
||||||
_sendBuffer = new ConcurrentQueue<byte[]>();
|
_sendBuffer = new ConcurrentQueue<byte[]>();
|
||||||
_ctsSource = new CancellationTokenSource();
|
_ctsSource = new CancellationTokenSource();
|
||||||
|
_receivedMessagesLock = new object();
|
||||||
|
|
||||||
_socket = CreateSocket();
|
_socket = CreateSocket();
|
||||||
}
|
}
|
||||||
@ -244,7 +270,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
public virtual void Send(string data)
|
public virtual void Send(string data)
|
||||||
{
|
{
|
||||||
if (_closing)
|
if (_closing)
|
||||||
throw new InvalidOperationException("Can't send data when socket is not connected");
|
throw new InvalidOperationException($"Socket {Id} Can't send data when socket is not connected");
|
||||||
|
|
||||||
var bytes = _encoding.GetBytes(data);
|
var bytes = _encoding.GetBytes(data);
|
||||||
log.Write(LogLevel.Trace, $"Socket {Id} Adding {bytes.Length} to sent buffer");
|
log.Write(LogLevel.Trace, $"Socket {Id} Adding {bytes.Length} to sent buffer");
|
||||||
@ -371,14 +397,14 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (start != null)
|
if (start != null)
|
||||||
log.Write(LogLevel.Trace, $"Websocket sent delayed {Math.Round((DateTime.UtcNow - start.Value).TotalMilliseconds)}ms because of rate limit");
|
log.Write(LogLevel.Trace, $"Socket {Id} sent delayed {Math.Round((DateTime.UtcNow - start.Value).TotalMilliseconds)}ms because of rate limit");
|
||||||
|
|
||||||
outgoingMessages.Add(DateTime.UtcNow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _socket.SendAsync(new ArraySegment<byte>(data, 0, data.Length), WebSocketMessageType.Text, true, _ctsSource.Token).ConfigureAwait(false);
|
await _socket.SendAsync(new ArraySegment<byte>(data, 0, data.Length), WebSocketMessageType.Text, true, _ctsSource.Token).ConfigureAwait(false);
|
||||||
|
_outgoingMessages.Add(DateTime.UtcNow);
|
||||||
|
log.Write(LogLevel.Trace, $"Socket {Id} sent {data.Length} bytes");
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@ -427,6 +453,8 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
{
|
{
|
||||||
receiveResult = await _socket.ReceiveAsync(buffer, _ctsSource.Token).ConfigureAwait(false);
|
receiveResult = await _socket.ReceiveAsync(buffer, _ctsSource.Token).ConfigureAwait(false);
|
||||||
received += receiveResult.Count;
|
received += receiveResult.Count;
|
||||||
|
lock(_receivedMessagesLock)
|
||||||
|
_receivedMessages.Add(DateTime.UtcNow, receiveResult.Count);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@ -462,20 +490,30 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
multiPartMessage = true;
|
multiPartMessage = true;
|
||||||
if (memoryStream == null)
|
if (memoryStream == null)
|
||||||
memoryStream = new MemoryStream();
|
memoryStream = new MemoryStream();
|
||||||
|
log.Write(LogLevel.Trace, $"Socket {Id} received {receiveResult.Count} bytes in partial message");
|
||||||
await memoryStream.WriteAsync(buffer.Array, buffer.Offset, receiveResult.Count).ConfigureAwait(false);
|
await memoryStream.WriteAsync(buffer.Array, buffer.Offset, receiveResult.Count).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!multiPartMessage)
|
if (!multiPartMessage)
|
||||||
|
{
|
||||||
// Received a complete message and it's not multi part
|
// Received a complete message and it's not multi part
|
||||||
|
log.Write(LogLevel.Trace, $"Socket {Id} received {receiveResult.Count} bytes in single message");
|
||||||
HandleMessage(buffer.Array, buffer.Offset, receiveResult.Count, receiveResult.MessageType);
|
HandleMessage(buffer.Array, buffer.Offset, receiveResult.Count, receiveResult.MessageType);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
// Received the end of a multipart message, write to memory stream for reassembling
|
// Received the end of a multipart message, write to memory stream for reassembling
|
||||||
|
log.Write(LogLevel.Trace, $"Socket {Id} received {receiveResult.Count} bytes in partial message");
|
||||||
await memoryStream!.WriteAsync(buffer.Array, buffer.Offset, receiveResult.Count).ConfigureAwait(false);
|
await memoryStream!.WriteAsync(buffer.Array, buffer.Offset, receiveResult.Count).ConfigureAwait(false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (_receivedMessagesLock)
|
||||||
|
UpdateReceivedMessages();
|
||||||
|
|
||||||
if (receiveResult?.MessageType == WebSocketMessageType.Close)
|
if (receiveResult?.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
// Received close message
|
// Received close message
|
||||||
@ -491,6 +529,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
if (multiPartMessage)
|
if (multiPartMessage)
|
||||||
{
|
{
|
||||||
// Reassemble complete message from memory stream
|
// Reassemble complete message from memory stream
|
||||||
|
log.Write(LogLevel.Trace, $"Socket {Id} reassembled message of {memoryStream!.Length} bytes");
|
||||||
HandleMessage(memoryStream!.ToArray(), 0, (int)memoryStream.Length, receiveResult.MessageType);
|
HandleMessage(memoryStream!.ToArray(), 0, (int)memoryStream.Length, receiveResult.MessageType);
|
||||||
memoryStream.Dispose();
|
memoryStream.Dispose();
|
||||||
}
|
}
|
||||||
@ -514,7 +553,9 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
strData = DataInterpreterBytes(data);
|
var relevantData = new byte[count];
|
||||||
|
Array.Copy(data, offset, relevantData, 0, count);
|
||||||
|
strData = DataInterpreterBytes(relevantData);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -619,8 +660,21 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
private int MessagesSentLastSecond()
|
private int MessagesSentLastSecond()
|
||||||
{
|
{
|
||||||
var testTime = DateTime.UtcNow;
|
var testTime = DateTime.UtcNow;
|
||||||
outgoingMessages.RemoveAll(r => testTime - r > TimeSpan.FromSeconds(1));
|
_outgoingMessages.RemoveAll(r => testTime - r > TimeSpan.FromSeconds(1));
|
||||||
return outgoingMessages.Count;
|
return _outgoingMessages.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void UpdateReceivedMessages()
|
||||||
|
{
|
||||||
|
var checkTime = DateTime.UtcNow;
|
||||||
|
if (checkTime - _lastReceivedMessagesUpdate > TimeSpan.FromSeconds(1))
|
||||||
|
{
|
||||||
|
foreach (var msgTime in _receivedMessages.Keys)
|
||||||
|
if (checkTime - msgTime > TimeSpan.FromSeconds(3))
|
||||||
|
_receivedMessages.Remove(msgTime);
|
||||||
|
|
||||||
|
_lastReceivedMessagesUpdate = checkTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
if (pausedActivity != value)
|
if (pausedActivity != value)
|
||||||
{
|
{
|
||||||
pausedActivity = value;
|
pausedActivity = value;
|
||||||
log.Write(LogLevel.Debug, "Paused activity: " + value);
|
log.Write(LogLevel.Debug, $"Socket {Socket.Id} Paused activity: " + value);
|
||||||
if(pausedActivity) ActivityPaused?.Invoke();
|
if(pausedActivity) ActivityPaused?.Invoke();
|
||||||
else ActivityUnpaused?.Invoke();
|
else ActivityUnpaused?.Invoke();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user