I’m looking for a correct way to continue receiving data via websocket for more than 24h.
With the code below, you can recreate a connection, but I think that doing it this way there is a possibility of losing data during the transition between websocket instances.
using System;
using System.Text;
using System.Threading;
using System.Net.WebSockets;
using System.Threading.Tasks;
namespace Reconnect
{
public class Program
{
public static async Task Main(string args)
{
var aaa = new BinanceHelper();
await aaa.ConnectAsync();
await Task.Delay(int.MaxValue);
}
}
public class BinanceHelper
{
private readonly System.Timers.Timer reconnectTimer;
private ClientWebSocket webSocket = new ClientWebSocket();
public BinanceHelper()
{
reconnectTimer = new System.Timers.Timer(15_000) { AutoReset = true };
reconnectTimer.Elapsed += async (sender, e) => await AutoReconnectAsync();
}
public async Task ConnectAsync()
{
reconnectTimer.Start();
await webSocket.ConnectAsync(new Uri("wss://stream.binance.com:9443/ws/btcusdt@depth5"), CancellationToken.None);
await Task.Factory.StartNew(ReceiveMessagesAsync,
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
public async Task ReceiveMessagesAsync()
{
var buffer = new byte[8192];
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
var response = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine(response);
}
}
}
private async Task AutoReconnectAsync()
{
if (webSocket.State == WebSocketState.Open)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Close", CancellationToken.None);
Console.WriteLine("here");
webSocket.Dispose();
webSocket = null;
webSocket = new ClientWebSocket();
await ConnectAsync();
}
}
}
}
Does anyone know a correct way to do this?
Hi @Conta_Grrla,
You’re correct that Binance WebSocket connections are automatically closed after 24 hours, requiring users to handle reconnections manually. To avoid losing data during the transition, the recommended approach is to establish a new connection before the old one is closed, allowing for a smooth handover of data.
Below is an implementation that:
- Reconnects every 23 hours (before the 24-hour limit).
- Briefly runs both connections in parallel to prevent data loss.
- Gracefully shuts down the old WebSocket after the new one starts receiving traffic.
- Note: Since both connections will receive data for a short period, some message duplication may occur. It is up to the user to handle duplicates appropriately.
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.WebSockets;
namespace Reconnect
{
public class Program
{
private static ClientWebSocket _webSocket;
private static CancellationTokenSource _cts;
private static Task _receiveTask;
private static Timer _reconnectTimer;
public static async Task Main(string[] args)
{
await StartWebSocket();
}
private static async Task StartWebSocket()
{
while (true)
{
_webSocket = new ClientWebSocket();
_cts = new CancellationTokenSource();
Console.WriteLine("Connecting to Binance WebSocket...");
await _webSocket.ConnectAsync(new Uri("wss://stream.binance.com:9443/ws/btcusdt@trade"), _cts.Token);
Console.WriteLine("Connected!");
_receiveTask = ReceiveMessagesAsync(_webSocket, _cts.Token);
// Schedule reconnection before 24-hour timeout (set for 23 hours)
_reconnectTimer = new Timer(async _ => await ReconnectWebSocket(), null, TimeSpan.FromHours(23), Timeout.InfiniteTimeSpan);
await _receiveTask;
}
}
private static async Task ReceiveMessagesAsync(ClientWebSocket webSocket, CancellationToken token)
{
var buffer = new byte[8192];
while (webSocket.State == WebSocketState.Open && !token.IsCancellationRequested)
{
try
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), token);
if (result.MessageType == WebSocketMessageType.Text)
{
var response = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine(response);
}
}
catch (Exception ex)
{
Console.WriteLine($"WebSocket error: {ex.Message}");
break;
}
}
}
private static async Task ReconnectWebSocket()
{
Console.WriteLine("Reconnecting WebSocket...");
// Create new WebSocket instance
var newWebSocket = new ClientWebSocket();
await newWebSocket.ConnectAsync(new Uri("wss://stream.binance.com:9443/ws/btcusdt@trade"), CancellationToken.None);
Console.WriteLine("New connection established!");
// Start receiving messages from the new WebSocket
var newReceiveTask = ReceiveMessagesAsync(newWebSocket, CancellationToken.None);
// Allow both connections to run briefly before closing the old one to avoid data loss
await Task.Delay(TimeSpan.FromSeconds(10));
// Cancel the old connection
_cts.Cancel();
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Reconnecting", CancellationToken.None);
_webSocket.Dispose();
// Replace old WebSocket and task references
_webSocket = newWebSocket;
_receiveTask = newReceiveTask;
// Reset reconnection timer
_reconnectTimer.Dispose();
_reconnectTimer = new Timer(async _ => await ReconnectWebSocket(), null, TimeSpan.FromHours(23), Timeout.InfiniteTimeSpan);
}
}
}
How it works:
- Initial WebSocket connection is established and starts receiving messages.
- A reconnection timer is set for 23 hours to create a new WebSocket before the old one expires.
- When the timer triggers, a new WebSocket connects while the old one is still running.
- Both connections run in parallel for 10 seconds, ensuring no data is lost.
- Since both connections receive traffic, some messages may be duplicated. The user must implement deduplication logic if necessary.
- The old WebSocket is then gracefully closed
- The process repeats automatically.
Let me know if the implementation above works for you!