在一个局域网中,许多系统都要求每台计算机能够保持时间的一致性,如WIN2000系统提供了与主域服务器时间同步的功能,即计算机登录到主域服务器,计算机系统的时间自动与主域服务器时间一致。那么又是如何使得主域服务器的时间同步世界标准时间的呢?
如果能够使用GPS卫星时钟获得毫秒级别的标准时间,那会是很棒的一件事,前提了你付了钱!另一个不错的选择是,我们可以连接到Internet,利用Internet上的标准时间服务器获得标准时间,虽然只有秒级精度,不过对我们来说足够了,因为我们几乎不会用电脑时间来做秒表的...
实际上,在Innternet上有三个不同的时间服务,每一个都由RFC(Request for Comment)定义为Internet时间标准。这三个标准分别是:RFC-867、RFC-868、RFC-1305。今天我学习的就是:RFC-867 DayTime协议(RFC867 DayTime Protocol)。
Daytime是互联网的一个标准协议,该协议不指定固定的传输格式,只要按照ASCII标准发送数据即可。它也是一个非常有用的测量和调试工具--测试电脑网络连通性。不过这个方法当前似乎有点非主流了(对吗?),因为现在人们更多的是使用ping或者traceroute。
它即可以使用TCP,也可以使用UDP,知名端口号是13。
下面是DayTime客户端和服务器程序(示例):
1.客户端:
#include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //winsock 使用的库函数 #define DAY_TIME_DEF_PORT 13 //默认端口 #define DAY_TIME_BUF_SIZE 64 //缓冲区大小 #define DAY_TIME_DEF_COUNT 2 //发送的次数 int main(int argc , char **argv) { WSADATA wsaData; SOCKET time_soc = 0; struct sockaddr_in peer_addr,serv_addr; int timeout = 2000; //接收超时,2s int i, result, send_len, addr_len = sizeof(serv_addr); char *dest = "127.0.0.1", *send_data = "Hello,DayTime!"; char recv_buf[ DAY_TIME_BUF_SIZE ]; if(argc == 2) dest = argv[1]; WSAStartup(WINSOCK_VERSION,&wsaData); //初始化Winsock资源 send_len = strlen(send_data); //填写服务器地址 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(DAY_TIME_DEF_PORT); serv_addr.sin_addr.s_addr = inet_addr(dest); //当然是转网络字节序 //错误判断,都是程序化的步骤(套路)... if(serv_addr.sin_addr.s_addr == INADDR_NONE) { printf("[DayTime]invaild address\r\n"); return -1; } //创建DayTime使用的Socket,注意是 “SOCK_DGRAM”类型,协议为:UDP:无连接、不可靠的传输服务 time_soc = socket(AF_INET,SOCK_DGRAM,0); result = setsockopt(time_soc,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout)); /*设置接收数据的超时值,这样做的目的是,一旦数据丢失,客户端不会收到服务器的数据。 客户端使用的是阻塞套接口,程序永远阻塞在 recvfrom函数,无法进行其他处理。 设置超时时间后,recvfrom函数会返回SOCKET_ERROR,程序会继续执行,重发数据。 */ /* UDP是不可靠的,下面使用循环,设置的是消息发送两次, */ for(i = 0;i<DAY_TIME_DEF_COUNT;i++) { //发送数据 result = sendto( time_soc,send_data,send_len,0,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); //接收应答 result = recvfrom( time_soc , recv_buf , DAY_TIME_BUF_SIZE , 0 , (struct sockaddr*)&peer_addr , &addr_len); if(result >= 0) { recv_buf[result] = 0; printf( " [Daytime]recv: \"%s\" ,from : %s\r\n" , recv_buf , inet_ntoa(peer_addr.sin_addr) ); } } closesocket(time_soc); WSACleanup(); return 0; }
2.服务器端:
#include<stdio.h> #include<winsock2.h> #include<time.h> #pragma comment(lib,"ws2_32.lib") #define DAY_TIME_DEF_PORT 13 #define DAY_TIME_BUF_SIZE 64 #define DAY_TIME_DEF_COUNT 2 int main(int argc , char **argv) { WSADATA wsaData; SOCKET serv_sock = 0; struct sockaddr_in serv_addr, //服务器端句柄 clnt_addr; //客户端句柄 unsigned short port = DAY_TIME_DEF_PORT; int result, addr_len = sizeof(serv_addr); char *time_str, recv_buf[ DAY_TIME_BUF_SIZE ]; time_t now_time; WSAStartup(WINSOCK_VERSION,&wsaData); //初始化 if(argc == 2) port = atoi(argv[1]); serv_sock = socket(AF_INET,SOCK_DGRAM,0); // serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); serv_addr.sin_addr.s_addr = INADDR_ANY; //调用bind为套接口绑定本地地址 result = bind(serv_sock,(struct sockaddr *)&serv_addr,addr_len); if( result == SOCKET_ERROR) { printf("[Daytime]bind error : %d",WSAGetLastError()); closesocket(serv_sock); return -1; } printf("[Daytime] the server is running...\n"); while(1) { result = recvfrom( serv_sock , recv_buf , DAY_TIME_BUF_SIZE , 0 , (struct sockaddr*)&clnt_addr , &addr_len); if(result >= 0) //表示收到对方数据 { recv_buf[result] = 0; printf( " [Daytime]recv: \"%s\" ,from : %s\r\n" , recv_buf , inet_ntoa(clnt_addr.sin_addr) ); now_time = time(0); //得到当前时间 time_str = ctime(&now_time);//转字符串 //向客户端发送当前的日期和时间字符串 sendto( serv_sock,time_str,strlen(time_str),0,(struct sockaddr*)&clnt_addr , addr_len); } } closesocket(serv_sock); WSACleanup(); return 0; }
运行结果: