2

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");
    }
}
4
  • Use a sniffer like wireshark or fiddler to help debug. Check body of response to see what the string actually looks like. Http uses TCP as the transport layer. Normal TCP Keep-Alive sends an packet with no data for the Keep-Alive. Have not looked at the Github code to see what actually is being used.
    – jdweng
    Commented Oct 16, 2020 at 14:04
  • @jdweng I tried to use wireshark but weirdly enough, the received messages are not captured using Websocket protocol, but TLSv1.2 instead. I couldn't read any meaning information besides that.
    – Saliom
    Commented Oct 16, 2020 at 21:25
  • HTTP protocol (websocket) will will not occur until the TLS validates. So you are probably seeing the TLS Authentication occurring, but failing. So the server may want TLS 1.3 instead of 1.2. Or the certificate is not probably loading or wrong encryption mode is being used. There doesn't appear to be any TLS options in your client. So it must be defaulting to the operating system (or kernel). So what version of core are you using. What kernel are you using for the machine?
    – jdweng
    Commented Oct 16, 2020 at 22:15
  • @jdweng I'm currently on Windows 10 (version 1903) using .NET Core 3.1. The most interesting part is that I still can receive functional data using that c# client. I also tried with two other similar Websocket servers (Bitmex and Bitfinex) and they both also appear in wireshark only sending TLSv1.2 packets. These servers however do not require to send ping/pong raw frames which is necessary for the first one.
    – Saliom
    Commented Oct 16, 2020 at 23:47

1 Answer 1

1

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

3
  • 2
    You are right, after investigating further in the source code, it's automatically responding to each ping frame with a pong. Also, the ClientWebSocket's KeepAliveInterval option is actually sending a pong frame each interval, not just basic TCP KeepAlive.
    – Saliom
    Commented May 29, 2021 at 15:07
  • The problem with ClientWebSocket is, that it only send pong (not ping) and don't wait for response. github.com/dotnet/runtime/issues/48729 Commented May 16, 2023 at 14:21
  • If you want to detect quickly problematic connection (and want to use ClientWebSocket) best is to create own ping messages. Current state - sending only pong and not checking response is pathetic. Commented May 16, 2023 at 16:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.