WinSock网络编程学习笔记(九):基于UDP实现DayTime协议

时间:2022-10-04 10:23:31
在一个局域网中,许多系统都要求每台计算机能够保持时间的一致性,如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;


}

运行结果:
WinSock网络编程学习笔记(九):基于UDP实现DayTime协议