利用I/O复用模型实现一个时间同步服务器
一、实验内容
- 服务端采用I/O复用模型(select函数)接收客户端的时间同步请求
- 服务端采用单线程,但要能同时接收多客户端的连接请 求,显示客户端IP和端口,并向其回送时间信息。
- 客户端尝试同时使用 UDP 和 TCP 来实现。
- 注:借助 I/O 复用模型,用单线程达到多线程的效果。
二、实验要求
提交源码(源码编写要规范)、可执行程序、实验报告(要有程序运行截图)。
三、实验分析
select 介绍
select函数
的作用是监听指定的多个I/O的文件描述符,在设定的时间内阻塞,当有一个或者多个I/O端口满足某个“读”或者“写”的条件,则在fd_set
类型参数中标记并返回。
fd_set类型
是一个默认大小为1024位的类型,每一位代表一个I/0文件描述符。例如每个进程默认0、1、2
分别表示标准输入、标准输出和标准错误输出,所以fd_set类型
1024位中0、1、2位
分别代表标准输入、标准输出和标准错误输出。同理假如进程里创建了一个监听socket文件描述符为3,则fd_set的第3位代表这个监听sockfd;假如用accept
了客户端连接返回一个fdSocket为n,那么fd_set
的第n个位就代表这个fdSocket。(因为在一个进程里,某个I/O口的文件描述符是唯一的。)
fd_set fdRead,fdSocket;
FD_ZERO (&fdSocket);//将fdset所有“位”置0
FD_SET(TcpServerSocket,&fdSocket);
FD_SET(UdpServerSocket,&fdSocket);
server
#include <iostream>
#include <WinSock2.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
using namespace std;
#pragma comment (lib,"ws2_32.lib")
#define BUFFER_LEN 1024
#define IP_ADDRESS "127.0.0.1"
#define PORT 8888
int main()
{
WSADATA Ws;
WORD sockVersion = MAKEWORD(2,2);
SOCKET TcpServerSocket = INVALID_SOCKET;
SOCKET UdpServerSocket = INVALID_SOCKET;
SOCKET ConnectSocket = INVALID_SOCKET;
if ( WSAStartup(sockVersion, &Ws) != 0 )
{
cout<<"Init Windows Socket Failed:"<<GetLastError()<<endl;
closesocket(ConnectSocket);
return -1;
}
sockaddr_in ClientAddr;
char recvbuf[BUFFER_LEN];
char sendbuf[BUFFER_LEN];
int iResult;
// 创建用于监听的套接字
TcpServerSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if ( TcpServerSocket == INVALID_SOCKET )
{
cout<<"Create Socket Failed:"<<GetLastError()<<endl;
closesocket(TcpServerSocket);
WSACleanup();
return -1;
}
// 为套接字绑定地址和端口号
// 监听端口为DEFAULT_PORT
struct sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(PORT);
serAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
memset(serAddr.sin_zero,0x00,8);
int Addrlen = sizeof(serAddr);
SYSTEMTIME time;
GetLocalTime(&time);
iResult = bind(TcpServerSocket, (struct sockaddr*)&serAddr, sizeof(serAddr));
if(iResult == SOCKET_ERROR)
{
cout<<"Bind Error !"<<endl;
return -1;
}
sockaddr_in remoteAddr;
int nAddrLen = sizeof(remoteAddr);
UdpServerSocket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
iResult = bind(UdpServerSocket,(struct sockaddr *)&serAddr,sizeof(serAddr));
if(iResult == SOCKET_ERROR)
{
cout<<"Bind Error !"<<endl;
return -1;
}
// 监听套接字
iResult = listen(TcpServerSocket,SOMAXCONN);
if(iResult == SOCKET_ERROR)
{
cout<<"---Listen Failed ---!"<<endl;
closesocket(TcpServerSocket);
WSACleanup();
return -1;
}
cout<<"---Server