UDP协议传输数据包并不可靠,但却比TCP更加高效。UDP协议在实时性要求较高的网络编程中得到了大量应用。其本身存在的不可靠性也可以通过应用层的相关协议来消除。
在刚刚基本了解Linux的网络编程后,昨天仿照着书上的例程编写了一个使用UDP协议获取NTP网络时间的小程序,并且测试通过。想着把程序贴出来,以后没准也能用得上。
要从NTP获取网络时间需要解析NTP的数据包,先打包一个NTP数据包上传给NTP服务器用于请求网络时间,NTP服务器随后会将时间数据通过数据包返回,在等待接收NTP返回数据包的过程中,应该使用select()方法对套接字文件描述符进行非阻塞检测是否有数据可读可写,当在指定时间内NTP返回了数据包,则select()方法会返回可读可写的套接字文件描述符个数,超时则自动返回0,select()方法在网络编程中很常用,而其使用方法在网上也很多,这里就不再介绍了。
下面是UDP编程获取NTP网络时间的C代码:
/* UDP network gets ntp time demo based on platform linux. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <netdb.h> #include <unistd.h> #include <time.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <sys/un.h> #include <sys/time.h> #include <sys/ioctl.h> #define TRUE 1 #define FALSE 0 #define NTP_PORT 123 #define NTP_PORT_STR "123" #define NTP_SERVER_ADDR "61.135.250.78" #define NTP_PCK_SIZE 48 #define NTPV1 1 #define NTPV2 2 #define NTPV3 3 #define NTPV4 4 #define JAN_1970 0x83AA7E80 #define NTP_FRAC(x) (4294 * (x) + ((1981 * (x)) >> 11)) #define USEC(x) (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16)) #define LI 0 #define VN NTPV3 #define MODE 3 /* client mode */ #define STRATUM 0 #define POLL 4 #define PREC -6 typedef struct _ntp_time { unsigned int coarse; unsigned int fine; } ntp_time; typedef struct _ntp_packet { unsigned char leap_ver_mode; unsigned char startnum; char poll; char precision; int root_delay; int root_dispersion; int reference_identifier; ntp_time reference_timestamp; ntp_time originage_timestamp; ntp_time receive_timestamp; ntp_time transmit_timestamp; } ntp_pack; int set_ntp_pack(char* packet) /* pack ntp network packet */ { long tmp_wrd; time_t timer; memset(packet, 0, NTP_PCK_SIZE); /* pack ntp head */ tmp_wrd = htonl((LI << 30) | (VN << 27) | (MODE << 24) | (STRATUM << 16) | (POLL << 8) | (PREC & 0XFF)); memcpy(packet, &tmp_wrd, sizeof(tmp_wrd)); /* set root delay and root dispersion and reference indentifier */ tmp_wrd = htonl(1 << 16); memcpy(&packet[4], &tmp_wrd, sizeof(tmp_wrd)); memcpy(&packet[8], &tmp_wrd, sizeof(tmp_wrd)); /* set timer stamp */ time(&timer); /* set transmit timestamp coarse */ tmp_wrd = htonl(JAN_1970 + (long)timer); memcpy(&packet[40], &tmp_wrd, sizeof(tmp_wrd)); /* set transmit timestamp fine */ tmp_wrd = htonl((long) NTP_FRAC(timer)); memcpy(&packet[44], &tmp_wrd, sizeof(tmp_wrd)); return NTP_PCK_SIZE; } int get_ntp_time(int sockfd, struct addrinfo* addr, ntp_pack* time_now) { char data[NTP_PCK_SIZE]; int packet_len, addr_len = addr->ai_addrlen; fd_set read_fdset; struct timeval block_time; packet_len = set_ntp_pack(data); /* send packet to ntp server */ if (sendto(sockfd, data, packet_len, 0, addr->ai_addr, addr_len) < 0) { printf ("send packet to ntp server failed\n"); return FALSE; } FD_ZERO(&read_fdset); FD_SET(sockfd, &read_fdset); /* add fd into fdset */ //FD_CLR(int fd, fd_set *fdset); /* remove fd from fdset */ //FD_ISSET(int fd, fd_set *fdset); /* test fd of fdset */ /* wait block for max 10 seconds */ block_time.tv_sec = 10; block_time.tv_usec = 0; /* ntp data received or not */ if (select(sockfd + 1, &read_fdset, NULL, NULL, &block_time) > 0) { /* receive packet from ntp server */ if (recvfrom(sockfd, data, NTP_PCK_SIZE, 0, addr->ai_addr, &addr_len) < NTP_PCK_SIZE) { printf ("receive packet from ntp server failed\n"); return FALSE; } /* read ntp server time packet */ time_now->leap_ver_mode = ntohl(data[0]); time_now->startnum = ntohl(data[1]); time_now->poll = ntohl(data[2]); time_now->precision = ntohl(data[3]); /* read integer type for 4 bytes */ time_now->root_delay = ntohl(*((int *)&data[4])); time_now->root_dispersion = ntohl(*((int *)&data[8])); time_now->reference_identifier = ntohl(*((int *)&data[12])); time_now->reference_timestamp.coarse = ntohl(*((int *)&data[16])); time_now->reference_timestamp.fine = ntohl(*((int *)&data[20])); time_now->originage_timestamp.coarse = ntohl(*((int *)&data[24])); time_now->originage_timestamp.fine = ntohl(*((int *)&data[28])); time_now->receive_timestamp.coarse = ntohl(*((int *)&data[32])); time_now->receive_timestamp.fine = ntohl(*((int *)&data[36])); time_now->transmit_timestamp.coarse = ntohl(*((int *)&data[40])); time_now->transmit_timestamp.fine = ntohl(*((int *)&data[44])); return TRUE; } return FALSE; } int set_local_time(ntp_pack* newtime) { struct timeval tv; tv.tv_sec = newtime->transmit_timestamp.coarse - JAN_1970; tv.tv_usec = USEC(newtime->transmit_timestamp.fine); return settimeofday(&tv, NULL); } int main (int argc, char* argv[]) { int sockfd; struct addrinfo hints, *res = NULL; ntp_pack nt_pack; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /* udp socket type */ hints.ai_protocol = IPPROTO_UDP; /* get ntp information */ if (getaddrinfo(NTP_SERVER_ADDR, NTP_PORT_STR, &hints, &res)) { printf ("get ntp information failed\n"); exit(0); } sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd < 0) { printf ("create new socket failed\n"); exit(1); } if (get_ntp_time(sockfd, res, &nt_pack)) { printf ("get ntp server time success\n"); if (!set_local_time(&nt_pack)) { /* set local time */ printf ("set local time success\n"); } } close(sockfd); /* close udp socket */ return TRUE; }