Pinging Websocket server with Websocket .NET client

Viewed 2898

I'm trying to figure out how to send ping/pong heartbeats in order to be sure to maintain a connection with a Websocket server using Websocket .NET client in a .NET Core WPF Application, so I'm testing with this Binance Websocket which says

The websocket server will send a ping frame every 3 minutes. If the websocket server does not receive a pong frame back from the connection within a 10 minute period, the connection will be disconnected. Unsolicited pong frames are allowed.

However, when I try to connect using my sample code, I simply never receive any message from the websocket that contains the string ping, and the connection seems to never fail if there is at least one stream subscribed to. Also sending unsolicited pong messages seems to always throw an error 3: invalid JSON followed by a disconnection.

Are ping/pong heartbeats managed by the NuGet Package (even though I didn't find any line that mentions ping or pong in its source code), or simply doesn't support them? If so then how/why is my connection to Binance Websocket not getting disconnected?

Here's my sample code for the WSClient class so you can try and see that you won't get disconnected even after +15mins:

Update: It's handled internally by the WebsocketClient, and the KeepAliveInterval is actually sending pong frames each interval. I wonder though if it's possible to subscribe some event to see when these frames are being sent, which could be useful when dealing with strict messages rates limitations to not get accidently disconnected when sending the max number of messages at the same time as the pong frame is being sent.

using System;
using System.Net.WebSockets;
using Websocket.Client;
using Websocket.Client.Models;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

public class WSClient
{
    private List<string> logs = new List<string>();

    private WebsocketClient client;
    private string wsUrl = "wss://stream.binance.com/ws";
    
    public WSClient() { }

    public async Task InitializeClient()
    {
        Uri uri = new Uri(wsUrl);
        Func<ClientWebSocket> factory = new Func<ClientWebSocket>(() => {
            ClientWebSocket wsClient = new ClientWebSocket
            {
                Options =
                {
                    KeepAliveInterval = TimeSpan.FromSeconds(5) // interval to send pong frames             }
            };

            return wsClient;
        });

        client = new WebsocketClient(uri, factory);
        client.Name = "WebsocketClient";
        client.ReconnectTimeout = TimeSpan.FromSeconds(30);
        client.ErrorReconnectTimeout = TimeSpan.FromSeconds(30);

        client.ReconnectionHappened.Subscribe(OnReconnection);
        client.DisconnectionHappened.Subscribe(OnDisconnect);

        client.MessageReceived
            .Where((ResponseMessage message) => message.MessageType == WebSocketMessageType.Text)
            .Subscribe(OnMessage);

        try
        {
            await client.StartOrFail();
        }
        catch (Exception ex)
        {
            logs.Add($"Error: { ex.Message }");
        }

        // Subscribing to a stream (btcusdt@aggTrade for example)
        client.Send("{\"method\": \"SUBSCRIBE\",\"params\":[\"btcusdt@aggTrade\"],\"id\": 1}");
    }

    private void OnReconnection(ReconnectionInfo info)
    {
        logs.Add("Reconnected");
    }

    private void OnMessage(ResponseMessage message)
    {
        logs.Add(message.Text);
    }

    private void OnDisconnect(DisconnectionInfo info)
    {
        logs.Add("Diconnected");
    }
}
1 Answers

I think the WebSocketClient is handling the ping-pong protocol under the covers. I believe you don't have to worry about it.

Related