如何编写NTP客户端?

我正在寻找用C语言编写NTP客户端的代码示例。我发现:修复了代码上的一些问题,并且能够编译。 但在“发送数据……”之后,它并没有什么。 我不知道如何解决这个问题。 代码或服务器的问题在哪里? 提前致谢。

/* * 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,如果是,则执行一些error handling。

对于其他系统调用,其他值可能表示错误。 有关详细信息,请参见man

这是一个运作良好的代码。 (根本不能正常工作:糟糕的时间戳 – 阅读我在这个页面中发布的androidpost)。

感谢分享。

  /* 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上进行了测试。这是您提供的Unix的Windows等效代码

 #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); } 

您应该在任何POSIX兼容系统上使用来自POSIX libc的recvfrom()函数,就像Unix那样。 或包容性Windows。 因为它支持一些基本的POSIXfunction。

阅读使用UDP套接字的手册,而不是面向连接。 他们有些特别。

祝你今天愉快。 并希望我的post可以帮到你。

干杯;

亚伯。

在这里你有一个完整的NTP客户端,我昨天根据你的代码进行了编码。

格拉西亚斯;)

它被移植到android,所以你必须改变一些function才能使它在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); */ }