一 实验内容
利用I/O复用模型实现一个时间同步服务器
1. 服务端采用I/O复用模型(select函数)接收客户端的时间同步请求;
服务端采用单线程,但要能同时接收多客户端的连接请求,显示客户端IP
2. 和端口,并向其回送时间信息。
3. 客户端尝试同时使用UDP和TCP来实现。
注:借助I/O复用模型,用单线程达到多线程的效果。
二 代码设计
Client
时间同步函数
SYSTEMTIME curtime;//声明结构体变量;
curtime.wHour = hour;
curtime.wMinute = min;
curtime.wSecond = sec;
//结构体变量curtime各项值别赋值
if (SetLocalTime(&curtime))
{
cout << "yes";
}
else
{
cout << "no";
}
TCP
//从服务器接受时间并输出
while (true)
{
cin >> SendBuffer;
Ret = send(ClientSocket, SendBuffer, (int)strlen(SendBuffer), 0);
if (Ret == SOCKET_ERROR)
{
cout << "Send Info Error::" << GetLastError() << endl;
break;
}
Ret = recv(ClientSocket,(char *)&hour , 4, 0);
Ret = recv(ClientSocket, (char *)&min, 4, 0);
Ret = recv(ClientSocket, (char *)&sec, 4, 0);
cout << hour << min <<sec<<endl;
}
UDP
//从服务器接受时间并输出
while (1)
{
cin >> sendData;
sendto(sclient, sendData, strlen(sendData), 0, (sockaddr *)&sin, len);
char recvData[255];
int ret = recvfrom(sclient, (char *)&hour, 4, 0, (sockaddr *)&sin, &len);
ret = recvfrom(sclient, (char *)&min, 4, 0, (sockaddr *)&sin, &len);
ret = recvfrom(sclient, (char *)&sec, 4, 0, (sockaddr *)&sin, &len);
if (ret > 0)
{
cout << hour << min << sec << endl;
}
}
Server
// 创建用于监听的套接字
ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);//TCP监听套接字
UDPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);//UDP套接字
//为两个套接字绑定同一个端口
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(DEFAULT_PORT); // 监听端口为DEFAULT_PORT
addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
iResult1 = bind(ServerSocket, (const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN));
iResult2 = bind(UDPSocket, (const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN));
//将绑定过端口的套接字加入套接字集合fdSocket中,可以观察到TCP监听套接字在[0]中,UDP套接字在[1]中
fd_set fdRead, fdSocket;
FD_ZERO(&fdSocket);
FD_SET(ServerSocket, &fdSocket);
FD_SET(UDPSocket, &fdSocket);
//用select函数挑选有响应的套接字
fdRead = fdSocket;
iResult = select(0, &fdRead, NULL, NULL, NULL);
//向集合中添加新socket
if (fdSocket.fd_array[i] == ServerSocket)
{
if (fdSocket.fd_count < FD_SETSIZE)
{
//同时复用的套接字数量不能大于FD_SETSIZE
//有新的连接请求
AcceptSocket = accept(ServerSocket, (sockaddr FAR*)&addrClient, &addrClientlen);
if (AcceptSocket == INVALID_SOCKET)
{
printf("accept failed !\n");
closesocket(ServerSocket);
WSACleanup();
return 1;
}
//增加新的连接套接字进行复用等待
FD_SET(AcceptSocket, &fdSocket);
printf("接收到新的连接:%s\n", inet_ntoa(addrClient.sin_addr));
}
else
{
printf("连接个数超限!\n");
continue;
}
}
发送数据阶段
//tcp数据处理
if (i != 1)
{
memset(recvbuf, 0, recvbuflen);
now = time(NULL);
tm = localtime(static_cast<const time_t*>(&now));
hour = tm->tm_hour;
min = tm->tm_min;
sec = tm->tm_sec;
//获取时间并传送
iResult = recv(fdSocket.fd_array[i], recvbuf, recvbuflen, 0);
Ret = send(fdSocket.fd_array[i], (const char *)&hour, 4, 0);
Ret = send(fdSocket.fd_array[i], (const char *)&min, 4, 0);
Ret = send(fdSocket.fd_array[i], (const char *)&sec, 4, 0);
if (Ret == SOCKET_ERROR)
{
cout << "Send Info Error::" << GetLastError() << endl;
break;
}
}
//udp数据处理
char recvData[255];
int ret = recvfrom(UDPSocket, recvData, 255, 0, (sockaddr *)&remoteAddr, &nAddrLen);
if (ret > 0)
{
recvData[ret] = 0x00;
}
// char * sendData = "一个来自服务端的UDP数据包\n";
now = time(NULL);
tm = localtime(static_cast<const time_t*>(&now));
hour = tm->tm_hour;
min = tm->tm_min;
sec = tm->tm_sec;
//获取时间并传送
sendto(fdSocket.fd_array[i], (const char *)&hour, 4, 0, (sockaddr *)&remoteAddr, nAddrLen);
sendto(fdSocket.fd_array[i], (const char *)&min, 4, 0, (sockaddr *)&remoteAddr, nAddrLen);
sendto(fdSocket.fd_array[i], (const char *)&sec, 4, 0, (sockaddr *)&remoteAddr, nAddrLen);
cout << "UDP时间发送成功!";
三 实验实现
UDP Client测试
TCP Client测试
TCP / UDP Client测试
修改时间成功
服务器的时间设为2013.5.5 5:17 此时客户端更改时间为下图