Как написать клиент NTP?

Я ищу пример кода клиента NTP, пишущего на C. Я нашел это : Исправлены некоторые вещи в коде, и он умеет компилировать. Но после «отправки данных ..» это ничего не значит. Я не знаю, как это исправить. Где проблема с кодом или сервером? Заранее спасибо.

/* * This code will query a ntp server for the local time and display * it. it is intended to show how to use a NTP server as a time * source for a simple network connected device. * This is the C version. The orignal was in Perl * * For better clock management see the offical NTP info at: * http://www.eecis.udel.edu/~ntp/ * * written by Tim Hogard (thogard@abnormal.com) * Thu Sep 26 13:35:41 EAST 2002 * Converted to C Fri Feb 21 21:42:49 EAST 2003 * this code is in the public domain. * it can be found here http://www.abnormal.com/~thogard/ntp/ * */ #include  #include  #include  #include  #include  #include  #include  #include  void ntpdate(); int main() { ntpdate(); return 0; } void ntpdate() { char *hostname="tick.usno.navy.mil"; int portno=123; //NTP is port 123 int maxlen=1024; //check our buffers int i; // misc var i unsigned char msg[48]={010,0,0,0,0,0,0,0,0}; // the packet we send unsigned long buf[maxlen]; // the buffer we get back //struct in_addr ipaddr; // struct protoent *proto; // struct sockaddr_in server_addr; int s; // socket int tmit; // the time -- This is a time_t sort of //use Socket; // //#we use the system call to open a UDP socket //socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die "socket: $!"; proto=getprotobyname("udp"); s=socket(PF_INET, SOCK_DGRAM, proto->p_proto); if(s) { perror("asd"); printf("socket=%d\n",s); } // //#convert hostname to ipaddress if needed //$ipaddr = inet_aton($HOSTNAME); memset( &server_addr, 0, sizeof( server_addr )); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr = inet_addr(hostname); //argv[1] ); //i = inet_aton(hostname,&server_addr.sin_addr); server_addr.sin_port=htons(portno); //printf("ipaddr (in hex): %x\n",server_addr.sin_addr); /* * build a message. Our message is all zeros except for a one in the * protocol version field * msg[] in binary is 00 001 000 00000000 * it should be a total of 48 bytes long */ // send the data printf("sending data..\n"); i=sendto(s,msg,sizeof(msg),0,(struct sockaddr *)&server_addr,sizeof(server_addr)); // get the data back i=recv(s,buf,sizeof(buf),0); printf("recvfr: %d\n",i); //perror("recvfr:"); //We get 12 long words back in Network order /* for(i=0;i<12;i++) printf("%d\t%-8x\n",i,ntohl(buf[i])); */ /* * The high word of transmit time is the 10th word we get back * tmit is the time in seconds not accounting for network delays which * should be way less than a second if this is a local NTP server */ tmit=ntohl((time_t)buf[10]); //# get transmit time //printf("tmit=%d\n",tmit); /* * Convert time to unix standard time NTP is number of seconds since 0000 * UT on 1 January 1900 unix time is seconds since 0000 UT on 1 January * 1970 There has been a trend to add a 2 leap seconds every 3 years. * Leap seconds are only an issue the last second of the month in June and * December if you don't try to set the clock then it can be ignored but * this is importaint to people who coordinate times with GPS clock sources. */ tmit-= 2208988800U; //printf("tmit=%d\n",tmit); /* use unix library function to show me the local time (it takes care * of timezone issues for both north and south of the equator and places * that do Summer time/ Daylight savings time. */ //#compare to system time printf("Time: %s",ctime(&tmit)); i=time(0); //printf("%d-%d=%d\n",i,tmit,i-tmit); printf("System time is %d seconds off\n",i-tmit); } 

Он не уверен, что sendto () блокирует.

Чтобы проверить это, вы можете добавить

 printf("receiving data..\n"); 

перед вызовом recv() .


Также вызов recv() может блокироваться.

Он ожидает байтов sizeof(buf) , который равен 1024* sizeof(long) .

Из комментариев в источнике мы узнаем, что ожидается 12 длинных слов, поэтому вы можете подумать об этом recv() , изменив вызов на recv() следующим образом:

 i = recv(s, buf, 12*sizeof(buf[0]), 0); 

Несмотря на это, я настоятельно рекомендую добавить проверку ошибок на системные вызовы, например sendo() и recv() . Чтобы сделать это для этих двух, проверьте значение, которое они возвращают, будет меньше 0 и, если это так, выполните некоторую обработку ошибок.

Для других системных вызовов другие значения могут указывать на ошибку. Подробнее см. man .

Вот код, который хорошо работает. (совсем не работает: плохая timestamp – прочитайте сообщение android на этой странице, сделанное мной).

Спасибо, что поделился.

  /* This code will query a ntp server for the local time and display * it. it is intended to show how to use a NTP server as a time * source for a simple network connected device. * This is the C version. The orignal was in Perl * * For better clock management see the offical NTP info at: * http://www.eecis.udel.edu/~ntp/ * * written by Tim Hogard (thogard@abnormal.com) * Thu Sep 26 13:35:41 EAST 2002 * Converted to C Fri Feb 21 21:42:49 EAST 2003 * this code is in the public domain. * it can be found here http://www.abnormal.com/~thogard/ntp/ * */ #include  #include  #include  #include  #include  #include  #include  #include  void ntpdate(); int main() { ntpdate(); return 0; } void ntpdate() { char *hostname="163.117.202.33"; int portno=123; //NTP is port 123 int maxlen=1024; //check our buffers int i; // misc var i unsigned char msg[48]={010,0,0,0,0,0,0,0,0}; // the packet we send unsigned long buf[maxlen]; // the buffer we get back //struct in_addr ipaddr; // struct protoent *proto; // struct sockaddr_in server_addr; int s; // socket int tmit; // the time -- This is a time_t sort of //use Socket; // //#we use the system call to open a UDP socket //socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die "socket: $!"; proto=getprotobyname("udp"); s=socket(PF_INET, SOCK_DGRAM, proto->p_proto); perror("socket"); // //#convert hostname to ipaddress if needed //$ipaddr = inet_aton($HOSTNAME); memset( &server_addr, 0, sizeof( server_addr )); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr = inet_addr(hostname); //argv[1] ); //i = inet_aton(hostname,&server_addr.sin_addr); server_addr.sin_port=htons(portno); //printf("ipaddr (in hex): %x\n",server_addr.sin_addr); /* * build a message. Our message is all zeros except for a one in the * protocol version field * msg[] in binary is 00 001 000 00000000 * it should be a total of 48 bytes long */ // send the data printf("sending data..\n"); i=sendto(s,msg,sizeof(msg),0,(struct sockaddr *)&server_addr,sizeof(server_addr)); perror("sendto"); // get the data back struct sockaddr saddr; socklen_t saddr_l = sizeof (saddr); i=recvfrom(s,buf,48,0,&saddr,&saddr_l); perror("recvfr:"); //We get 12 long words back in Network order /* for(i=0;i<12;i++) printf("%d\t%-8x\n",i,ntohl(buf[i])); */ /* * The high word of transmit time is the 10th word we get back * tmit is the time in seconds not accounting for network delays which * should be way less than a second if this is a local NTP server */ tmit=ntohl((time_t)buf[10]); //# get transmit time //printf("tmit=%d\n",tmit); /* * Convert time to unix standard time NTP is number of seconds since 0000 * UT on 1 January 1900 unix time is seconds since 0000 UT on 1 January * 1970 There has been a trend to add a 2 leap seconds every 3 years. * Leap seconds are only an issue the last second of the month in June and * December if you don't try to set the clock then it can be ignored but * this is importaint to people who coordinate times with GPS clock sources. */ tmit-= 2208988800U; //printf("tmit=%d\n",tmit); /* use unix library function to show me the local time (it takes care * of timezone issues for both north and south of the equator and places * that do Summer time/ Daylight savings time. */ //#compare to system time printf("Time: %s",ctime(&tmit)); i=time(0); //printf("%d-%d=%d\n",i,tmit,i-tmit); printf("System time is %d seconds off\n",i-tmit); } 

И вот вывод:

 $ ./sntp_client socket: Success sending data.. sendto: Success recvfr:: Success Time: Sun Sep 26 16:00:32 4213 System time is -702316565 seconds off 

Вот код, который меня модифицировал и компилирует и выполняет.
Он протестирован на сервере 2003, VS 2008. Это эквивалентный код Windows для Unix, который вы предоставили

 #include  //#include  //#include  //#include  //#include  //#include  #include  #include  #include  #include  #include  #pragma comment(lib, "Ws2_32.lib") void ntpdate(); int main() { ntpdate(); return 0; } void ntpdate() { char *hostname="64.27.26.1";//"tick.usno.navy.mil"; int portno=123; //NTP is port 123 int maxlen=1024; //check our buffers long i; // misc var i char msg[48]={010,0,0,0,0,0,0,0,0}; // the packet we send char *buf = new char[1024]; // the buffer we get back //struct in_addr ipaddr; // struct protoent *proto; // struct sockaddr_in server_addr; SOCKET s; // socket time_t tmit; // the time -- This is a time_t sort of //===================================================================================== //THIS IS WHAT IS MISSING MAJORILY //===================================================================================== //Initialise the winsock stack WSADATA wsaData; BYTE wsMajorVersion = 1; BYTE wsMinorVersion = 1; WORD wVersionRequested = MAKEWORD(wsMinorVersion, wsMajorVersion); if (WSAStartup(wVersionRequested, &wsaData) != 0) { _tprintf(_T("Failed to load winsock stack\n")); WSACleanup(); return; } if (LOBYTE(wsaData.wVersion) != wsMajorVersion || HIBYTE(wsaData.wVersion) != wsMinorVersion) { _tprintf(_T("Winsock stack does not support version which this program requires\n")); WSACleanup(); return; } //===================================================================================== //use Socket; // //#we use the system call to open a UDP socket //socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die "socket: $!"; proto=getprotobyname("udp"); int err = GetLastError(); s=socket(PF_INET, SOCK_DGRAM, proto->p_proto); if(s) { perror("asd"); printf("socket=%d\n",s); } // //#convert hostname to ipaddress if needed //$ipaddr = inet_aton($HOSTNAME); memset( &server_addr, 0, sizeof( server_addr )); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr = inet_addr(hostname); //argv[1] ); //i = inet_aton(hostname,&server_addr.sin_addr); server_addr.sin_port=htons(portno); //printf("ipaddr (in hex): %x\n",server_addr.sin_addr); /* * build a message. Our message is all zeros except for a one in the * protocol version field * msg[] in binary is 00 001 000 00000000 * it should be a total of 48 bytes long */ // send the data printf("sending data..\n"); i=sendto(s,msg,sizeof(msg),0,(struct sockaddr *)&server_addr,sizeof(server_addr)); int iResult = -1; // Receive until the peer closes the connection //do { iResult = recv(s, buf, 1024, 0); if ( iResult > 0 ) printf("Bytes received: %d\n", iResult); else if ( iResult == 0 ) printf("Connection closed\n"); else printf("recv failed: %d\n", WSAGetLastError()); //} while( iResult > 0 ); /* * The high word of transmit time is the 10th word we get back * tmit is the time in seconds not accounting for network delays which * should be way less than a second if this is a local NTP server */ tmit=ntohl((time_t)buf[10]); //# get transmit time //printf("tmit=%d\n",tmit); /* * Convert time to unix standard time NTP is number of seconds since 0000 * UT on 1 January 1900 unix time is seconds since 0000 UT on 1 January * 1970 There has been a trend to add a 2 leap seconds every 3 years. * Leap seconds are only an issue the last second of the month in June and * December if you don't try to set the clock then it can be ignored but * this is importaint to people who coordinate times with GPS clock sources. */ tmit -= 2208988800U; //printf("tmit=%d\n",tmit); /* use unix library function to show me the local time (it takes care * of timezone issues for both north and south of the equator and places * that do Summer time/ Daylight savings time. */ //#compare to system time printf("Time: %s",ctime(&tmit)); i=time(0); //printf("%d-%d=%d\n",i,tmit,i-tmit); printf("System time is %d seconds off\n",i-tmit); } 

вы должны использовать функцию recvfrom() из POSIX libc в любой совместимой с POSIX системе, как Unix. Или включительно Windows. Поскольку он поддерживает некоторые базовые функции POSIX.

прочитайте руководство по использованию сокетов UDP, не ориентированных на соединения. они особенные.

хорошего дня. и надеюсь, что мой пост поможет вам.

веселит;

Авель.

Здесь у вас есть полный клиент NTP, который я закодировал вчера, основываясь на вашем коде.

gracias;)

он портирован на андроид, поэтому вам придется изменить какую-то функцию, чтобы заставить ее работать под Linux и раскомментировать некоторые main (), не так ли?

  /* This code will query a ntp server for the local time and display * it. it is intended to show how to use a NTP server as a time * source for a simple network connected device. * This is the C version. The orignal was in Perl * * For better clock management see the offical NTP info at: * http://www.eecis.udel.edu/~ntp/ * * written by Tim Hogard (thogard@abnormal.com) * Thu Sep 26 13:35:41 EAST 2002 * Converted to C Fri Feb 21 21:42:49 EAST 2003 * this code is in the public domain. * it can be found here http://www.abnormal.com/~thogard/ntp/ * * ported to android 4.3 on mar/nov/5 - 2013 by abel. * the same day ported to a library for agpsd layer. */ /* #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #define NTP_MODE_CLIENT 3 #define NTP_VERSION 3 #define TRANSMIT_TIME_OFFSET 40 #define REFERENCE_TIME_OFFSET 16 #define ORIGINATE_TIME_OFFSET 24 #define RECEIVE_TIME_OFFSET 32 #define OFFSET_1900_TO_1970 ((uint64_t)((365 * 70) + 17) * 24 * 60 * 60) */ void ntpdate(uint64_t *cachedTime, uint64_t *cachedTimeRef, uint64_t *cacheCertainty); /* int main() { uint64_t cachedTime, cachedTimeRef, cacheCertainty; ntpdate(&cachedTime, &cachedTimeRef, &cacheCertainty); printf ("%lld\n, %lld\n, %lld\n", cachedTime, cachedTimeRef, cacheCertainty); return 0; } */ double random2 () { srandom(time(NULL)); return random(); } uint64_t currentTimeMillis(/*long int seconds, long int miliseconds*/) { struct timeval te; gettimeofday(&te, NULL); // get current time uint64_t millis = (uint64_t) te.tv_sec * 1000 + floor(te.tv_usec / 1000); // caculate milliseconds // printf ("millis: %llu\n", millis); return millis; } uint32_t read32(char* buffer, int offset) { char b0 = buffer[offset]; char b1 = buffer[offset+1]; char b2 = buffer[offset+2]; char b3 = buffer[offset+3]; // convert signed bytes to unsigned values uint32_t i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0); uint32_t i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1); uint32_t i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2); uint32_t i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3); uint32_t v = (i0 << 24) + (i1 << 16) + (i2 << 8) + i3; return v; } uint64_t readTimeStamp(char *buffer, int offset) { uint32_t seconds = read32(buffer, offset); uint32_t fraction = read32(buffer, offset + 4); uint64_t v = ((int64_t)seconds - OFFSET_1900_TO_1970) * 1000 + (int64_t) fraction * 1000 / (int64_t) 0x100000000; // printf ("%llu\n", v); return v; } void writeTimeStamp(char* buffer, int offset, long long int time) { uint64_t seconds = floor (time / 1000); uint64_t milliseconds = time - (uint64_t) seconds * 1000; seconds += OFFSET_1900_TO_1970; // write seconds in big endian format buffer[offset++] = (char)(seconds >> 24); buffer[offset++] = (char)(seconds >> 16); buffer[offset++] = (char)(seconds >> 8); buffer[offset++] = (char)(seconds >> 0); uint64_t fraction = round ((uint64_t)milliseconds * 0x100000000 / 1000); // write fraction in big endian format buffer[offset++] = (char)(fraction >> 24); buffer[offset++] = (char)(fraction >> 16); buffer[offset++] = (char)(fraction >> 8); // low order bits should be random data buffer[offset++] = (char)(random2() * 255.0); } void ntpdate(uint64_t *cachedTime, uint64_t *cachedTimeRef, uint64_t *cacheCertainty) { char hostname[]="81.184.154.182"; int portno=123; //NTP is port 123 int maxlen=48; //check our buffers int i; // misc var i uint64_t requestTime = currentTimeMillis(); uint64_t requestTicks = android::elapsedRealtime(); char msg[48]; memset (msg,0,sizeof(msg)); msg[0]= NTP_MODE_CLIENT | (NTP_VERSION << 3); writeTimeStamp(msg, TRANSMIT_TIME_OFFSET, requestTime); char buf[maxlen]; // the buffer we get back struct sockaddr_in server_addr; int s; // socket time_t tmit; // the time -- This is a time_t sort of s=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); memset( &server_addr, 0, sizeof( server_addr )); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr = inet_addr(hostname); server_addr.sin_port=htons(portno); i=sendto(s,msg,sizeof(msg),0,(struct sockaddr *)&server_addr,sizeof(server_addr)); struct sockaddr saddr; socklen_t saddr_l = sizeof (saddr); i=recvfrom(s,buf,sizeof(buf),0,&saddr,&saddr_l); uint64_t responseTicks = android::elapsedRealtime(); uint64_t responseTime = requestTime + (responseTicks - requestTicks); uint64_t originateTime = readTimeStamp(buf, ORIGINATE_TIME_OFFSET); uint64_t receiveTime = readTimeStamp(buf, RECEIVE_TIME_OFFSET); uint64_t transmitTime = readTimeStamp(buf, TRANSMIT_TIME_OFFSET); uint64_t roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime); int32_t clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2; //printf ("%lld + %lld = %ld %ld\n", (receiveTime - originateTime), (transmitTime - responseTime), (receiveTime - originateTime + transmitTime - responseTime)/2, clockOffset); uint64_t mNtpTime = responseTime + clockOffset; uint64_t mNtpTimeReference = responseTicks; uint64_t mRoundTripTime = roundTripTime; uint64_t mCachedNtpTime = mNtpTime; uint64_t mCachedNtpElapsedRealtime = mNtpTimeReference; uint64_t mCachedNtpCertainty = mRoundTripTime / 2; *cachedTime = mCachedNtpTime; *cachedTimeRef = mNtpTimeReference; *cacheCertainty = mCachedNtpCertainty; // uint64_t now = mNtpTime + android::elapsedRealtime() - mNtpTimeReference; /* printf ("%lld\n", now); i=currentTimeMillis(); printf("System time is %lu miliseconds off\n",i-now); */ }