Search code examples
clinuxsocketstimestampudp

How to send transmit timestamps along with data payload?


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!


Solution

  • 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));