-1

I'm trying to send the transmit timestamp of a packet to calculate the latency. Thanks to Linux, UDP datagrams, and kernel timestamps: Lots of examples and stackoversflow entries later, and still cannot get timestamps at all, I can get timestamp when datagrams are sent and received. However, the demo in this entry can only get the transmit timestamp in sender and get the receive timestamp in receiver. I wonder that how can I send datagrams along with the transmit timestamp so that I can get both transmit and receive timestamps in receiver? I have read the https://www.kernel.org/doc/Documentation/networking/timestamping.txt docs but I have not found something that may help. Can anyone have ideas about it?

Thanks!

gtyss
  • 1
  • 1
  • You may be interested in [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm) – user3386109 Mar 26 '21 at 04:05

1 Answers1

1

You have to store the bytes of the timestamp in your message (at the beginning is the best, in your own header ideally, along with other important information). See example below:

Client

#include <stdio.h>      // Default System Calls
#include <stdlib.h>     // Needed for OS X
#include <string.h>     // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h>       // To control the timeout mechanism

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror("socket failed");
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );
    serveraddr.sin_addr.s_addr = htonl( 0x7f000001 );


    int n = (int)time(NULL);
    unsigned char timestamp[4];
    timestamp[0] = (n >> 24) & 0xFF;
    timestamp[1] = (n >> 16) & 0xFF;
    timestamp[2] = (n >> 8) & 0xFF;
    timestamp[3] = n & 0xFF;

    unsigned char buffer[256];
    memset(buffer, 0, sizeof(buffer));
    unsigned char s[6] = "hello\0";
    memcpy(&buffer, &timestamp, 4);
    memcpy(&buffer[4], &s, strlen(s));


    for ( int i = 0; i < 2; i++ ) {
        if (sendto( fd, buffer, strlen(buffer), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
            perror( "sendto failed" );
            break;
        }
        printf("message sent\n" );
    }

    close( fd );
}

Server

#include <stdio.h>      // Default System Calls
#include <stdlib.h>     // Needed for OS X
#include <string.h>     // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h>       // To control the timeout mechanism

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror( "socket failed" );
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );
    serveraddr.sin_addr.s_addr = htonl( INADDR_ANY );

    if ( bind(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
        perror( "bind failed" );
        return 1;
    }

    char buffer[200];
    for ( int i = 0; i < 100; i++ ) {
        int length = recvfrom( fd, buffer, sizeof(buffer) - 1, 0, NULL, 0 );
        if ( length < 0 ) {
            perror( "recvfrom failed" );
            break;
        }
        buffer[length] = '\0';
        int n = (int)time(NULL);
        int timestamp = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; // get the timestamp from the message

        printf( "%dsd ago - %d bytes (4 bytes for timestamp): '%s'\n", n - timestamp, length, buffer+4 );
    }

    close( fd );
}

NB: credit: https://stackoverflow.com/a/35570418/3161139 for UDP server/client "hello world"

NB2: Note that you should compute the timestamp and memcpy it to the buffer right before sending the message, and not before the loop, but hopefully you get the general idea

NB3: As chux suggests you could avoid truncating:

time_t n = time(NULL); 
int len = sizeof(n);
unsigned char timestamp[len];
// fill timestamp with n

...

memcpy(&buffer, &timestamp, len);
memcpy(&buffer[len], &s, strlen(s));
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
  • 1
    Why truncate `time_t` to an `int` with `int n = (int)time(NULL);`? Could use `time_t n = time(NULL);` and `unsigned char timestamp[sizeof n];`. – chux - Reinstate Monica Mar 26 '21 at 06:19
  • The example works well, thanks very much! Another thing, If I use some flags such as SOF_TIMESTAMPING_TX_HARDWARE or something else in setting the socket, I can get the timestamp, which may be generated by the network adaptor, which records the time exactly when the packet is sent or received. Is there any way can make it possible to send this timestamp with my data from client to server, so that I can get more accurate timestamps? – gtyss Mar 26 '21 at 06:50
  • I am not sure about `SOF_TIMESTAMPING_TX_HARDWARE` but I think it calls timestamp under the hood and perform the same kind of what I shared with you. Glad it was helpful! If it solved your problem you may select it as answer! – Antonin GAVREL Mar 26 '21 at 07:08