mikerochip/unity-websocket

Full Ping-Pong implementation

Closed this issue · 5 comments

I have to write my ping pong on top of yours in order to measure the time to the server.
It would be cool if you added your ping-pong code and displayed a public property with the latest delay time.

My temp code now looks like this:

        public TimeSpan PingInterval { get; set; } = TimeSpan.FromSeconds(1);
        private string _pingMessage = "p";
        private float _pingSendTimer;
        private string FullPingMessage => _pingMessage + _pingPacketSequence;
        private readonly Stopwatch _pingTimer = new Stopwatch();
        private byte _pingPacketSequence;

          private void Ping(){
            _pingSendTimer += Time.deltaTime;
            if (_pingSendTimer >= PingInterval.Seconds)
            {
                Debug.Log("[PP] Send ping...");
                //reset timer
                _pingSendTimer = 0;
                //send ping
                if(_pingPacketSequence > 250) _pingPacketSequence = 0;
                _pingPacketSequence++;
                _pingTimer.Restart();
                SendString(FullPingMessage);
            }}

           
         // called from OnMessageReceived
         private void Pong(string pongMessage)
        {
            if (pongMessage != FullPingMessage) return;
            _pingTimer.Stop();
            int elapsedMs = (int)_pingTimer.ElapsedMilliseconds;
            
            Debug.Log("[PP] Received pong... ms "+ elapsedMs);
            LastLatency = elapsedMs / 2);
        }           

Hi. I don't think I understand exactly what you're asking for. Let me try to rephrase so I can understand it better: You want a built-in feature where messages are sent on an interval, and for the time deltas between the intervals to be exposed, so you can approximate an average round trip time.

Is that correct? I'm guessing you want to measure and compare player latency for matchmaking or something? That's not the purpose of a ping pong; the purpose of ping pong is for keep alives. In your pseudocode, the only similarity is that it happens on an interval. I would say that this is a new feature request where you want to re-use the ping pong system to solve another problem.

It seems like the real problem is that you don't have enough communication about what's happening with the ping pongs? If you had new events that got sent when the pings were sent and when the pongs were received, would that be enough to solve the problem so that you don't feel the need to roll a custom ping pong replacement? Something like this

public delegate void PingSentHandler(WebSocketConnection connection);
public delegate void PongReceivedHandler(WebSocketConnection connection);

public event PingSentHandler PingSent;
public event PongReceivedHandler PongReceived;

Do you also need each ping messages to be custom? One goal of my code is to prevent users from mutating state once a connection starts, except for sending messages and disconnecting, so I would say that if you want custom messages, that's basically a new feature that requires new state.

I'm hesitant about it, but I could add

public TimeSpan LastPingPongInterval { get; private set; }

to help reduce how much timing code you need to maintain; this would still fit into the goals of my code. My hesitation is that it's getting specific to your use case.

wdyt?

You are absolutely right that ping pong is used to keep the connection active (keep-alive). However, by measuring the time between sending a ping message to the server and receiving the same message in response from it, we can get important information about the delay between the client and the server. This information can be very useful in various situations, for example:

  • To evaluate the quality of the connection.
  • To implement load balancing mechanisms.
  • To improve the gaming experience in multiplayer games where low latency is critical.

Your code already implements sending PingMessage to the server and processing its receipt in the onMessageReceived method. It only remains to add a time measurement between these events. This will allow you to:

  • Get the exact value of the delay between the client and the server.
  • Use this information for various purposes, for example, to display the delay to the user or to make decisions in the system.

For me, it will be enough to add a property to your code that will store the last delay (LastPingPongInterval).
For example:

public int LastPingPongInterval { get; private set; } // in ms

Do you also need each ping messages to be custom? One goal of my code is to prevent users from mutating state once a connection starts, except for sending messages and disconnecting, so I would say that if you want custom messages, that's basically a new feature that requires new state.

This is not at all necessary. In the example from the first comment, I added int to PingMessage because there is a possibility that the message may arrive earlier than the previous one.

I agree on the utility of this request, just wondering if this should be a new feature vs exposing more information about the existing ping pong feature.

I'll expose a new property, but it'll be a TimeSpan as I wrote previously

public TimeSpan LastPingPongInterval { get; private set; }

you should be able to easily use .TotalMilliseconds from that. I will probably go ahead and add new events for ping pong too since those messages are currently not broadcast through any event mechanism.

Curious about your comment on messages arriving earlier than previous ones ... Websocket is built on top of http and tcp, so that shouldn't be possible assuming a single connection to a single server. Maybe you have some other setup that can lead to out of order processing, but ... it's none of my business, just a curiosity!

@Ficksik After looking at implementing this for the past couple days, the feature you're requesting is actually a big departure from ping pong. The fundamental difference is that ping pongs don't track specific messages, but that is what you said you wanted re: "by measuring the time between sending a ping message to the server and receiving the same message in response from it".

Ping pongs can be sent out of order, pongs are allowed to be sent multiple times and are expected to be ignored, etc. The current implementation makes an attempt to follow the spec from a behavior perspective so that the same code can be used from WebGL and non-WebGL builds.

So I think what you're asking for is basically a new feature like "average latency instrumentation"

TLDR If you want this to go faster, you can open a PR, and I can take a look. Your sample code is actually not matching sent messages to received messages, so I can't currently use it as a reference, unfortunately.

I don't think there's anything wrong with turning off the built-in one and keeping your custom one for the time being. I'm willing to build this in, but you'd have to wait a bit longer, probably.

I'm going to consider this implemented as of this release https://github.com/mikerochip/unity-websocket/releases/tag/3.0.0

I believe you have what you need with the new functionality to implement what you want. You will have to track an average of RTTs on your own, so that part isn't being done for you, but I think that's it.

Please let me know if something's missing.