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

Compare commits

..

16 Commits

Author SHA1 Message Date
Jan Korf d06bd5f176 Updated version 2022-05-24 18:56:37 +02:00
Jan Korf d55fc8da65 Merge pull request #144 from tamaw/fix/missing-port-on-baseuri
Fixed: copying the port number when using a custom BaseAddress
2022-05-24 15:14:38 +02:00
Tama Waddell 01184f2c5d Added port to the other overloaded method 2022-05-24 21:13:08 +10:00
Jan Korf cadc93c2f0 Merge pull request #143 from andriibratanin/bugfix/fix-nuget-discovery
Fix NuGet packages discovery for some IDEs
2022-05-24 10:21:21 +02:00
Tama Waddell 2600a51461 Included copying the port when using SetParameters 2022-05-24 15:16:06 +10:00
Andrii Bratanin 9e6a86ba8b Fix wrong case in csproj files of tests projects #142 2022-05-24 01:31:25 +03:00
Jan Korf c4430d63fa Added KeepAliveInterval setting for socket connections 2022-05-23 22:05:04 +02:00
Jan Korf f3e1cfef33 Updated version 2022-05-22 15:51:48 +02:00
Jan Korf cc3053719c Make socket ConnectionLost run in a separate task to prevent issue with long running/exceptions in the handler 2022-05-22 15:46:30 +02:00
Jan Korf cd6907e601 Merge branch 'master' of https://github.com/jkorf/CryptoExchange.Net 2022-05-22 14:35:07 +02:00
Jan Korf 8fe00693bd Merge pull request #141 from nathan-datusarator/master
Add checks for Disposed
2022-05-22 11:41:55 +02:00
Jan Korf fb90d1e015 Fixed exception when disposing client in reconnecting state 2022-05-22 11:35:39 +02:00
Jan Korf 4b44861e43 Added additional cases for no null/default handling in DateTimeConverter 2022-05-22 11:35:17 +02:00
Jan Korf e42ca4ab5a Update FAQ.md 2022-05-21 10:17:59 +02:00
Nathan Pfluger 5b97f6dd67 Move Subscription Events into non-lambda so they can be removed on StopAsync 2022-05-12 10:00:44 -07:00
Nathan Pfluger a9813ecb0a Add checks for Disposed 2022-05-12 09:05:27 -07:00
11 changed files with 106 additions and 33 deletions
@@ -6,10 +6,10 @@
</PropertyGroup>
<ItemGroup>
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.1.0-preview-20211130-02"></packagereference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0-preview-20211130-02"></PackageReference>
<PackageReference Include="Moq" Version="4.16.1" />
<packagereference Include="NUnit" Version="3.13.2"></packagereference>
<packagereference Include="NUnit3TestAdapter" Version="4.2.0"></packagereference>
<PackageReference Include="NUnit" Version="3.13.2"></PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0"></PackageReference>
</ItemGroup>
<ItemGroup>
@@ -40,6 +40,10 @@ namespace CryptoExchange.Net
/// </summary>
protected int MaxSocketConnections { get; set; } = 9999;
/// <summary>
/// Keep alive interval for websocket connection
/// </summary>
protected TimeSpan KeepAliveInterval { get; set; } = TimeSpan.FromSeconds(10);
/// <summary>
/// Delegate used for processing byte data received from socket connections before it is processed by handlers
/// </summary>
protected Func<byte[], string>? dataInterpreterBytes;
@@ -574,6 +578,7 @@ namespace CryptoExchange.Net
if (ClientOptions.Proxy != null)
socket.SetProxy(ClientOptions.Proxy);
socket.KeepAliveInterval = KeepAliveInterval;
socket.Timeout = ClientOptions.SocketNoDataTimeout;
socket.DataInterpreterBytes = dataInterpreterBytes;
socket.DataInterpreterString = dataInterpreterString;
@@ -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
+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.9</PackageVersion>
<AssemblyVersion>5.1.9</AssemblyVersion>
<FileVersion>5.1.9</FileVersion>
<PackageVersion>5.1.11</PackageVersion>
<AssemblyVersion>5.1.11</AssemblyVersion>
<FileVersion>5.1.11</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.9 - Added latency to the timesync calculation, Small fix for exception in socket close handling</PackageReleaseNotes>
<PackageReleaseNotes>5.1.11 - Added KeepAliveInterval setting, Fixed port not being copied when setting parameters on request, Fixed inconsistent PackageReference casing in csproj</PackageReleaseNotes>
<Nullable>enable</Nullable>
<LangVersion>9.0</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
+2
View File
@@ -426,6 +426,7 @@ namespace CryptoExchange.Net
var uriBuilder = new UriBuilder();
uriBuilder.Scheme = baseUri.Scheme;
uriBuilder.Host = baseUri.Host;
uriBuilder.Port = baseUri.Port;
uriBuilder.Path = baseUri.AbsolutePath;
var httpValueCollection = HttpUtility.ParseQueryString(string.Empty);
foreach (var parameter in parameters)
@@ -454,6 +455,7 @@ namespace CryptoExchange.Net
var uriBuilder = new UriBuilder();
uriBuilder.Scheme = baseUri.Scheme;
uriBuilder.Host = baseUri.Host;
uriBuilder.Port = baseUri.Port;
uriBuilder.Path = baseUri.AbsolutePath;
var httpValueCollection = HttpUtility.ParseQueryString(string.Empty);
foreach (var parameter in parameters)
@@ -77,6 +77,10 @@ namespace CryptoExchange.Net.Interfaces
/// </summary>
TimeSpan Timeout { get; set; }
/// <summary>
/// The interval at which to send a ping frame to the server
/// </summary>
TimeSpan KeepAliveInterval { get; set; }
/// <summary>
/// Set a proxy to use when connecting
/// </summary>
/// <param name="proxy"></param>
+28 -16
View File
@@ -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)
@@ -122,6 +122,9 @@ namespace CryptoExchange.Net.Sockets
/// <inheritdoc />
public TimeSpan Timeout { get; set; }
/// <inheritdoc />
public TimeSpan KeepAliveInterval { get; set; }
/// <inheritdoc />
public double IncomingKbps
{
@@ -332,7 +335,7 @@ namespace CryptoExchange.Net.Sockets
socket.Options.Cookies = cookieContainer;
foreach (var header in headers)
socket.Options.SetRequestHeader(header.Key, header.Value);
socket.Options.KeepAliveInterval = TimeSpan.FromSeconds(10);
socket.Options.KeepAliveInterval = KeepAliveInterval;
socket.Options.SetBuffer(65536, 65536); // Setting it to anything bigger than 65536 throws an exception in .net framework
return socket;
}
+34 -7
View File
@@ -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
}
}
}
+11
View File
@@ -18,6 +18,17 @@ 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.11 - 24 May 2022
* Added KeepAliveInterval setting
* Fixed port not being copied when setting parameters on request
* Fixed inconsistent PackageReference casing in csproj
* 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
+4 -1
View File
@@ -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.