How to receive websocket data for more than 24h?

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:

  1. Initial WebSocket connection is established and starts receiving messages.
  2. A reconnection timer is set for 23 hours to create a new WebSocket before the old one expires.
  3. When the timer triggers, a new WebSocket connects while the old one is still running.
  4. Both connections run in parallel for 10 seconds, ensuring no data is lost.
  5. Since both connections receive traffic, some messages may be duplicated. The user must implement deduplication logic if necessary.
  6. The old WebSocket is then gracefully closed
  7. The process repeats automatically.

Let me know if the implementation above works for you!