SimplePing calculating Trip Times

First of all, I love the new version of SimplePing that was released! It's a great new rewrite of a codebase that has been a longstanding part of our application for a long time. Ping works great and simple right out of the box.


There's one feature that I wished it had though, the ability to show the trip time of a ping. Much like the terminal ping command, I need to be able to see how long it takes from the point that a ping is sent to when it is received. Since we're talking about milliseconds here, I would like to create a way that uses the least amount of overhead, to show the most accurate pings possible. Does anyone have a recommendation on how to do this with SimplePing?


Thanks,

Matthew

Replies

Within the bounds of SimplePing, you can calculate round trip times by keeping a map of sequence numbers to send times. When you get the

-simplePing:didSendPacket:sequenceNumber:
delegate callback, add an entry to that map with the current time. When you get the
-simplePing:didReceivePingResponsePacket:sequenceNumber:
, use the sequence number to look up the send time, diff that against the current time to calculate the round trip, then remove the map entry.

Note If you plan to keep the pinger running for a long time, you should periodically garbage collect the map, removing entries whose start time is more than 2 minutes old (2 minutes being the standard ‘how long can a packet be bouncing around the Internet’ value).

It would probably be a good idea to schedule the SimplePing object on a secondary thread’s run loop so that work on the main thread doesn’t mess up your timing.

This should be sufficiently accurate for workaday Internet pings. If you need much better accuracy, you’d probably want to move away from the SimplePing architecture. Specifically, you’d want to try to get more accurate timestamps for both the send and receive events:

  • On the receive side you can use

    SO_TIMESTAMP
    to get the kernel to timestamp the packet as it comes in. This isolates you from the delay taken for the packet to wend it’s way from the kernel to your code.
  • On the send side there’s not a lot you can do; there’s no equivalent of

    SO_TIMESTAMP
    on the send side.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

On the send side there’s not a lot you can do; there’s no equivalent of  SO_TIMESTAMP on the send side.

/sbin/ping solves the problem by calling gettimeofday() and putting the result into the data field of the outgoing packet, because as of RFC 792 the Echo Reply packet has to contain the data field from the Echo Request.

Right, but that’s not equivalent SO_TIMESTAMP because SO_TIMESTAMP is applied by the kernel when the packet arrives, giving you an accurate timestamp even if there’s a long delay getting the packet from the kernel to your user space code. Consider this scenario:

  1. Call gettimeofday.

  2. Build Echo request.

  3. Write Echo request to socket.

  4. Echo request hits the ‘wire’.

If there’s any delay between 1 and 4 — for example, the kernel preempts your thread and goes off to run some other thread — you interpret that as a wire delay, which is incorrect.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Correct, it doesn't solve the delay between 1 and 4. It does solve the problem of keeping a map of sequence numbers and send times though.