1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-15 18:33:06 +00:00

Feature/websocket sequencing (#267)

Added EnforceSequenceNumbers property on SocketApiClient to configure whether websocket message contain sequence numbers and if these should be checked to be sequential
Added fallback to existing websocket connection if no dedicated request connection was found
Added IntBoolConverter base class for arbitrary int value to bool mapping
Added SequenceNumber property to DataEvent object
Added _skipSequenceCheckFirstUpdateAfterSnapshotSet property for SymbolOrderBook implementations
Updated SymbolOrderBook sequenceNumber validation
Updated SymbolOrderBook log verbosities
Renamed SetInitialOrderBook to SetSnapshot in SymbolOrderBook
Renamed updateId references to sequenceNumber in SymbolOrderBook
This commit is contained in:
Jan Korf
2026-01-12 14:26:50 +01:00
committed by GitHub
parent c512bee825
commit 3b2a85d210
7 changed files with 237 additions and 85 deletions
@@ -257,6 +257,7 @@ namespace CryptoExchange.Net.Sockets.Default
}
}
private bool _pausedActivity;
#if NET9_0_OR_GREATER
private readonly Lock _listenersLock = new Lock();
@@ -274,6 +275,8 @@ namespace CryptoExchange.Net.Sockets.Default
private ISocketMessageHandler? _byteMessageConverter;
private ISocketMessageHandler? _textMessageConverter;
private long _lastSequenceNumber;
/// <summary>
/// The task that is sending periodic data on the websocket. Can be used for sending Ping messages every x seconds or similar. Not necessary.
/// </summary>
@@ -293,7 +296,7 @@ namespace CryptoExchange.Net.Sockets.Default
/// Cache for deserialization, only caches for a single message
/// </summary>
private readonly Dictionary<Type, object> _deserializationCache = new Dictionary<Type, object>();
/// <summary>
/// New socket connection
/// </summary>
@@ -340,6 +343,7 @@ namespace CryptoExchange.Net.Sockets.Default
{
Status = SocketStatus.Closed;
Authenticated = false;
_lastSequenceNumber = 0;
if (ApiClient._socketConnections.ContainsKey(SocketId))
ApiClient._socketConnections.TryRemove(SocketId, out _);
@@ -371,6 +375,7 @@ namespace CryptoExchange.Net.Sockets.Default
Status = SocketStatus.Reconnecting;
DisconnectTime = DateTime.UtcNow;
Authenticated = false;
_lastSequenceNumber = 0;
lock (_listenersLock)
{
@@ -1280,6 +1285,23 @@ namespace CryptoExchange.Net.Sockets.Default
return result;
}
/// <summary>
/// Update the sequence number for this connection
/// </summary>
public void UpdateSequenceNumber(long sequenceNumber)
{
if (ApiClient.EnforceSequenceNumbers
&& _lastSequenceNumber != 0
&& _lastSequenceNumber + 1 != sequenceNumber)
{
// Not sequential
_logger.LogWarning("[Sckt {SocketId}] update not in sequence. Last recorded sequence number: {LastSequence}, update sequence number: {UpdateSequence}. Reconnecting", SocketId, _lastSequenceNumber, sequenceNumber);
_ = TriggerReconnectAsync();
}
_lastSequenceNumber = sequenceNumber;
}
/// <summary>
/// Periodically sends data over a socket connection
/// </summary>