mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-12 00:43:03 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f3e1cfef33 | |||
| cc3053719c | |||
| cd6907e601 | |||
| 8fe00693bd | |||
| fb90d1e015 | |||
| 4b44861e43 | |||
| e42ca4ab5a | |||
| 5b97f6dd67 | |||
| a9813ecb0a | |||
| c7069a4049 | |||
| 5683ae0b3c | |||
| 1c8cf5ac98 |
@@ -92,7 +92,7 @@ namespace CryptoExchange.Net
|
||||
}
|
||||
|
||||
// Calculate time offset between local and server
|
||||
var offset = result.Data - localTime;
|
||||
var offset = result.Data - (localTime.AddMilliseconds(result.ResponseTime!.Value.TotalMilliseconds / 2));
|
||||
timeSyncParams.UpdateTimeOffset(offset);
|
||||
timeSyncParams.TimeSyncState.Semaphore.Release();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace CryptoExchange.Net.Converters
|
||||
if(reader.TokenType is JsonToken.Integer)
|
||||
{
|
||||
var longValue = (long)reader.Value;
|
||||
if (longValue == 0)
|
||||
if (longValue == 0 || longValue == -1)
|
||||
return objectType == typeof(DateTime) ? default(DateTime): null;
|
||||
if (longValue < 19999999999)
|
||||
return ConvertFromSeconds(longValue);
|
||||
@@ -45,6 +45,9 @@ namespace CryptoExchange.Net.Converters
|
||||
else if (reader.TokenType is JsonToken.Float)
|
||||
{
|
||||
var doubleValue = (double)reader.Value;
|
||||
if (doubleValue == 0 || doubleValue == -1)
|
||||
return objectType == typeof(DateTime) ? default(DateTime) : null;
|
||||
|
||||
if (doubleValue < 19999999999)
|
||||
return ConvertFromSeconds(doubleValue);
|
||||
|
||||
@@ -56,6 +59,9 @@ namespace CryptoExchange.Net.Converters
|
||||
if (string.IsNullOrWhiteSpace(stringValue))
|
||||
return null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(stringValue) || stringValue == "0" || stringValue == "-1")
|
||||
return objectType == typeof(DateTime) ? default(DateTime) : null;
|
||||
|
||||
if (stringValue.Length == 8)
|
||||
{
|
||||
// Parse 20211103 format
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<PackageId>CryptoExchange.Net</PackageId>
|
||||
<Authors>JKorf</Authors>
|
||||
<Description>A base package for implementing cryptocurrency API's</Description>
|
||||
<PackageVersion>5.1.8</PackageVersion>
|
||||
<AssemblyVersion>5.1.8</AssemblyVersion>
|
||||
<FileVersion>5.1.8</FileVersion>
|
||||
<PackageVersion>5.1.10</PackageVersion>
|
||||
<AssemblyVersion>5.1.10</AssemblyVersion>
|
||||
<FileVersion>5.1.10</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.8 - Cleanup socket code, fixed an issue which could cause connections to never reconnect when connection was lost, Added support for sending requests which expect an empty response, Fixed issue with the DateTimeConverter date interpretation</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>5.1.10 - Fixed order book reconnecting while Diposed, Fixed exception when disposing socket client while reconnecting, Added additional null/default checking in DateTimeConverter, Changed ConnectionLost subscription event to run in seperate task to prevent exception/longer operations from intervering with reconnecting</PackageReleaseNotes>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
||||
@@ -258,24 +258,32 @@ namespace CryptoExchange.Net.OrderBook
|
||||
}
|
||||
|
||||
_subscription = startResult.Data;
|
||||
_subscription.ConnectionLost += () =>
|
||||
{
|
||||
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} connection lost");
|
||||
Status = OrderBookStatus.Reconnecting;
|
||||
Reset();
|
||||
};
|
||||
_subscription.ConnectionClosed += () =>
|
||||
{
|
||||
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} disconnected");
|
||||
Status = OrderBookStatus.Disconnected;
|
||||
_ = StopAsync();
|
||||
};
|
||||
_subscription.ConnectionLost += HandleConnectionLost;
|
||||
_subscription.ConnectionClosed += HandleConnectionClosed;
|
||||
_subscription.ConnectionRestored += HandleConnectionRestored;
|
||||
|
||||
_subscription.ConnectionRestored += async time => await ResyncAsync().ConfigureAwait(false);
|
||||
Status = OrderBookStatus.Synced;
|
||||
return new CallResult<bool>(true);
|
||||
}
|
||||
|
||||
private void HandleConnectionLost() {
|
||||
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} connection lost");
|
||||
if (Status != OrderBookStatus.Disposed) {
|
||||
Status = OrderBookStatus.Reconnecting;
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleConnectionClosed() {
|
||||
log.Write(LogLevel.Warning, $"{Id} order book {Symbol} disconnected");
|
||||
Status = OrderBookStatus.Disconnected;
|
||||
_ = StopAsync();
|
||||
}
|
||||
|
||||
private async void HandleConnectionRestored(TimeSpan _) {
|
||||
await ResyncAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task StopAsync()
|
||||
{
|
||||
@@ -286,8 +294,12 @@ namespace CryptoExchange.Net.OrderBook
|
||||
if (_processTask != null)
|
||||
await _processTask.ConfigureAwait(false);
|
||||
|
||||
if (_subscription != null)
|
||||
if (_subscription != null) {
|
||||
await _subscription.CloseAsync().ConfigureAwait(false);
|
||||
_subscription.ConnectionLost -= HandleConnectionLost;
|
||||
_subscription.ConnectionClosed -= HandleConnectionClosed;
|
||||
_subscription.ConnectionRestored -= HandleConnectionRestored;
|
||||
}
|
||||
log.Write(LogLevel.Trace, $"{Id} order book {Symbol} stopped");
|
||||
}
|
||||
|
||||
@@ -601,13 +613,13 @@ namespace CryptoExchange.Net.OrderBook
|
||||
|
||||
private async Task ProcessQueue()
|
||||
{
|
||||
while (Status != OrderBookStatus.Disconnected)
|
||||
while (Status != OrderBookStatus.Disconnected && Status != OrderBookStatus.Disposed)
|
||||
{
|
||||
await _queueEvent.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
while (_processQueue.TryDequeue(out var item))
|
||||
{
|
||||
if (Status == OrderBookStatus.Disconnected)
|
||||
if (Status == OrderBookStatus.Disconnected || Status == OrderBookStatus.Disposed)
|
||||
break;
|
||||
|
||||
if (_stopProcessing)
|
||||
|
||||
@@ -280,8 +280,14 @@ namespace CryptoExchange.Net.Sockets
|
||||
_sendEvent.Set();
|
||||
|
||||
if (_socket.State == WebSocketState.Open)
|
||||
await _socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", default).ConfigureAwait(false);
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
await _socket.CloseOutputAsync(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);
|
||||
}
|
||||
|
||||
@@ -141,6 +141,8 @@ namespace CryptoExchange.Net.Sockets
|
||||
private readonly List<PendingRequest> pendingRequests;
|
||||
private Task? _socketProcessReconnectTask;
|
||||
|
||||
private SocketStatus _status;
|
||||
|
||||
/// <summary>
|
||||
/// The underlying websocket
|
||||
/// </summary>
|
||||
@@ -217,10 +219,24 @@ namespace CryptoExchange.Net.Sockets
|
||||
subscription.CancellationTokenRegistration.Value.Dispose();
|
||||
}
|
||||
}
|
||||
await _socket.CloseAsync().ConfigureAwait(false);
|
||||
|
||||
if (_socketProcessReconnectTask != null)
|
||||
await _socketProcessReconnectTask.ConfigureAwait(false);
|
||||
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);
|
||||
}
|
||||
|
||||
_socket.Dispose();
|
||||
}
|
||||
@@ -232,7 +248,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// <returns></returns>
|
||||
public async Task CloseAsync(SocketSubscription subscription)
|
||||
{
|
||||
if (!_socket.IsOpen)
|
||||
if (!_socket.IsOpen || _status == SocketStatus.Disposed)
|
||||
return;
|
||||
|
||||
if (subscription.CancellationTokenRegistration.HasValue)
|
||||
@@ -255,9 +271,11 @@ namespace CryptoExchange.Net.Sockets
|
||||
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");
|
||||
});
|
||||
@@ -283,7 +301,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
if (!lostTriggered)
|
||||
{
|
||||
lostTriggered = true;
|
||||
ConnectionLost?.Invoke();
|
||||
_ = Task.Run(() => ConnectionLost?.Invoke());
|
||||
}
|
||||
|
||||
while (ShouldReconnect)
|
||||
@@ -372,7 +390,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
if (lostTriggered)
|
||||
{
|
||||
lostTriggered = false;
|
||||
_ = Task.Run(() => ConnectionRestored?.Invoke(time.HasValue ? DateTime.UtcNow - time.Value : TimeSpan.FromSeconds(0))).ConfigureAwait(false);
|
||||
_ = Task.Run(() => ConnectionRestored?.Invoke(time.HasValue ? DateTime.UtcNow - time.Value : TimeSpan.FromSeconds(0)));
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -396,6 +414,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_status = SocketStatus.Disposed;
|
||||
_socket.Dispose();
|
||||
}
|
||||
|
||||
@@ -458,7 +477,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
|
||||
var total = DateTime.UtcNow - timestamp;
|
||||
if (userProcessTime.TotalMilliseconds > 500)
|
||||
log.Write(LogLevel.Debug, $"Socket {SocketId} message processing slow ({(int)total.TotalMilliseconds}ms), consider offloading data handling to another thread. " +
|
||||
log.Write(LogLevel.Debug, $"Socket {SocketId} message processing slow ({(int)total.TotalMilliseconds}ms, {(int)userProcessTime.TotalMilliseconds}ms user code), consider offloading data handling to another thread. " +
|
||||
"Data from this socket may arrive late or not at all if message processing is continuously slow.");
|
||||
|
||||
log.Write(LogLevel.Trace, $"Socket {SocketId} message processed in {(int)total.TotalMilliseconds}ms, ({(int)userProcessTime.TotalMilliseconds}ms user code)");
|
||||
@@ -671,5 +690,13 @@ namespace CryptoExchange.Net.Sockets
|
||||
|
||||
return await socketClient.SubscribeAndWaitAsync(this, socketSubscription.Request!, socketSubscription).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private enum SocketStatus
|
||||
{
|
||||
None,
|
||||
Processing,
|
||||
Reconnecting,
|
||||
Disposed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,16 @@ 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.10 - 22 May 2022
|
||||
* Fixed order book reconnecting while Diposed
|
||||
* Fixed exception when disposing socket client while reconnecting
|
||||
* Added additional null/default checking in DateTimeConverter
|
||||
* Changed ConnectionLost subscription event to run in seperate task to prevent exception/longer operations from intervering with reconnecting
|
||||
|
||||
* Version 5.1.9 - 08 May 2022
|
||||
* Added latency to the timesync calculation
|
||||
* Small fix for exception in socket close handling
|
||||
|
||||
* Version 5.1.8 - 01 May 2022
|
||||
* Cleanup socket code, fixed an issue which could cause connections to never reconnect when connection was lost
|
||||
* Added support for sending requests which expect an empty response
|
||||
|
||||
+4
-1
@@ -61,4 +61,7 @@ var client = new BinanceClient(new BinanceClientOptions
|
||||
BaseAddress = BinanceApiAddresses.TestNet.UsdFuturesRestClientAddress
|
||||
}
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
### How are timezones handled / Timestamps are off by xx
|
||||
Exchange API's treat all timestamps as UTC, both incoming and outgoing. The client libraries do no conversion so be sure to use UTC as well.
|
||||
Reference in New Issue
Block a user