//------------------------------------------------------------------- // triptime.cpp // // This example demonstrates use of Linux's 'timestamp' socket // option to obtain a measurement of the round-trip time taken // by a small network packet sent to an echo-server running on // a remote host and then returned to this client application. // It shows how to access the 'ancillary data' returned by the // 'recvmsg()' function in an architecture-independent manner. // (You can test this program using our 'msgserver.cpp' demo.) // // compile using: $ g++ triptime.cpp -o triptime // execute using: $ ./triptime // // NOTE: This timestamp socket option provided by Linux should // not be confused with timestamp request/reply ICMP messages. // // programmer: ALLAN CRUSE // written on: 15 FEB 2009 //------------------------------------------------------------------- #include // for gethostbyname() #include // for printf(), perror() #include // for exit() #include // for strncpy() #include // for inet_ntoa() #include // for gettimeofday() int main( int argc, char **argv ) { //--------------------------------------------------------- // check for the required number of command-line arguments //--------------------------------------------------------- if ( argc < 3 ) { fprintf( stderr, "usage: ./triptime \n" ); exit(1); } //--------------------------------------------------------------- // get the target hostname and port-number from the command-line //--------------------------------------------------------------- char peername[ 64 ] = { 0 }; int port = atoi( argv[2] ); strncpy( peername, argv[1], 63 ); //------------------------------------------------- // obtain the internet address for the remote host //------------------------------------------------- char peeraddr[ 16 ] = { 0 }; struct hostent *pp = gethostbyname( peername ); if ( !pp ) { herror( "gethostbyname" ); exit(1); } strcpy( peeraddr, inet_ntoa( *(in_addr*)pp->h_addr ) ); //-------------------------------------------------------- // initialize a socket-address object for the remote host //-------------------------------------------------------- struct sockaddr_in paddr = { 0 }; socklen_t palen = sizeof( paddr ); paddr.sin_family = AF_INET; paddr.sin_port = htons( port ); paddr.sin_addr.s_addr = *(uint32_t*)pp->h_addr; //---------------------------------- // open an internet datagram socket //---------------------------------- int sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( sock < 0 ) { perror( "socket" ); exit(1); } //------------------------------------- // set a timeout for receiving replies //------------------------------------- struct timeval myto = { 5, 0 }; int tlen = sizeof( myto ); if ( setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, &myto, tlen ) < 0 ) { perror( "setsockopt RCVTIMEO" ); exit(1); } //--------------------------------------- // enable the SO_TIMESTAMP socket-option //--------------------------------------- int oval = 1; int olen = sizeof( oval ); if ( setsockopt( sock, SOL_SOCKET, SO_TIMESTAMP, &oval, olen ) < 0 ) { perror( "setsockopt TIMESTAMP"); exit(1); } //----------------------------------------------------- // initialize objects for our measage-header structure //----------------------------------------------------- char msg[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 ="; char buf[] = "----------------------------------------"; int len = strlen( msg ); struct iovec myiov[1] = { { msg, len } }; unsigned char cbuf[ 40 ] = { 0 }; int clen = sizeof( cbuf ); //----------------------------------------- // initialize our message-header structure //----------------------------------------- struct msghdr mymsghdr = { 0 }; mymsghdr.msg_name = &paddr; mymsghdr.msg_namelen = palen; mymsghdr.msg_iov = myiov; mymsghdr.msg_iovlen = 1; mymsghdr.msg_control = NULL; mymsghdr.msg_controllen = 0; mymsghdr.msg_flags = 0; //------------------------------------- // get this system's current timestamp //------------------------------------- struct timeval tvsend, tvrecv; if ( gettimeofday( &tvsend, NULL ) < 0 ) { perror( "gettimeofday" ); exit(1); } //------------------------------------------------------ // then immediately send our message to the remote host //------------------------------------------------------ int tx = sendmsg( sock, &mymsghdr, 0 ); if ( tx < 0 ) { perror( "sendmsg" ); exit(1); } //----------------------------------------------------- // now adjust some fields in our message-header object //----------------------------------------------------- myiov[0].iov_base = buf; mymsghdr.msg_control = cbuf; mymsghdr.msg_controllen = clen; //-------------------------------------------------------- // receive a reply from our echo-server (or else timeout) //-------------------------------------------------------- int rx = recvmsg( sock, &mymsghdr, 0 ); if ( rx < 0 ) { perror( "recvmsg" ); exit(1); } //------------------------------------------------------- // Apply system macros for accessing the ancilliary data //------------------------------------------------------- struct msghdr *msgp = &mymsghdr; struct cmsghdr *cmsg; for ( cmsg = CMSG_FIRSTHDR( msgp ); cmsg != NULL; cmsg = CMSG_NXTHDR( msgp, cmsg ) ) { if (( cmsg->cmsg_level == SOL_SOCKET ) &&( cmsg->cmsg_type == SO_TIMESTAMP )) memcpy( &tvrecv, CMSG_DATA( cmsg ), tlen ); } //----------------------------- // compute the round-trip time //----------------------------- unsigned long long stamp0, stamp1; stamp0 = tvsend.tv_sec * 1000000LL + tvsend.tv_usec; stamp1 = tvrecv.tv_sec * 1000000LL + tvrecv.tv_usec; unsigned long long rtt = stamp1 - stamp0; //------------------------------------- // report this transaction to the user //------------------------------------- printf( "\n contacted server on \'%s\' (%s) \n", peername, peeraddr ); printf( " %d bytes sent, %d bytes received \n", tx, rx ); printf( " round-trip time was %llu microseconds \n\n", rtt ); }