基于Select模型的服务器
1. 结构体SOCKET_INFORMATION
结构体SOCKET_INFORMATION用于记录服务器与每个客户端之间进行通信的信息,代码如下:
typedef struct _SOCKET_INFORMATION{
CHAR Buffer[DATA_BUFSIZE];//发送和接收数据的缓冲区
WSABUF DataBuf; //定义发送和接受数据缓冲区的结构体,包括长度和内容
SOCKET Socket; //与客户端进行通信的socket
DWORD BytesSEND; //保存Socket发送的字节数
DWORD BytesRECV; //保存Socket接收的字节数
}SOCKET_INFORMATION, *LPSOCKET_INFORMATION;
结构体SOCKET_INFORMATION中包含用于通信的Socket、发送和接收数据的缓冲区、发送和接收的字节数等。
WSABUF是winsock2.h中定义的结构体, 用于保存缓冲区地址和长度,代码如下:
typedef struct _WSABUF{
u_long len; //缓冲区的长度
char FAR* buf; //指向缓冲区的指针
}
2. 定义变量
为了对所有与客户端进行通信的Socket信息进行统一管理,需要定义下面两个变量:
//记录正在使用的套接字总数量
DWORD TotalSockets = 0;
//保存Socket信息对象的数组, FD_SETSIZE表示SELECT模型中允许的最大的Socket数量
LPSOCKET_INFORMATION SocketArray[FD_SETSIZE];
3. 创建Socket信息
调用CreateSocketInformation()函数可以为指定的Socket创建对应的SOCKET_INFORMATION结构体, 并将其添加到SocketArray数组中, 代码如下:
BOOL CreateSocketInformation(SOCKET s){
LPSOCKET_INFORMATION SI;
//为SI分配内存空间
if((SI = (LPSOCKET_INFORMATION)GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION)) == NULL){
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return FALSE;
}
//初始化SI的值
SI->Socket = s;
SI->BytesSEND = 0;
SI->BytesRECV = 0;
//在SocketArray数组中添加一个新的元素, 用户保存SI对象
SocketArray[TotalSockets++] = SI;
return TRUE;
}
4. 释放Socket信息
调用FreeSocketInformation()函数可以删除指定的Socket对应的SOCKET_INFORMATION结构体, 并关闭该Socket, 代码如下:
void FreeSocketInformation(DWORD Index){
//获取指定索引对应的LPSOCKET_INFORMATION
LPSOCKET_INFORMATION SI = SocketArray[Index];
DWORD i;
closesocket(SI->Socket); //关闭Socket
//释放指定的LPSOCKET_INFORMATION资源
GlobalFree(SI);
//将数组中的Index索引后面的元素前移
for(i = Index; i < TotalSockets; i++){
SocketArray[i] = SocketArray[i+1];
}
TotalSockets--;
}
5. 主函数的变量定义
int _tmain(int argc, _TCHAR* argv[]){
SOCKET ListenSocket; //监听socket
SOCKET AcceptSOcket; //与客户端进行通信的socket
SOCKADDR_IN InternetAddr; //服务器地址
WSADATA wsaData; //用户初始化Socket环境
INT Ret; //Winsock API的返回值
FD_SET WriteSet; //获取可写性Socket集合
FD_SET ReadSet; //获取可读性Socket集合
DWORD Total = 0; //处于就绪状态的Socket数量
DWORD SendBytes; //发送字节数
DWORD RecvBytes; //接收字节数
}
6. 在主函数中启动监听客户端的连接请求
主函数首先初始化Windows Sockets环境,并启动监听客户端的连接请求, 代码如下:
int _tmain(int argc, _TCHAR* argv[]){
....
//初始化WinSock环境
if((Ret = WSAStartup(0x0202, &wsaData)) != 0){
printf("WSAStartup() failed with error %d\n", Ret);
WSACleanup();
reutrn -1;
}
//创建用于监听的Socket
if((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET){
printf("WSASocket() failed with error %d\n", WSAGetLastError());
return -1;
}
//设置监听地址和端口号
InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(PORT);
//绑定监听Socket到本地地址和端口
if(bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR){
printf("bind() failed with error %d\n", WSAGetLastError());
return -1;
}
//开始监听
if(listen(ListenSocket, 5)){
printf("listen() failed with error %d\n", WSAGetLastError());
return -1;
}
//设置为非阻塞模式
ULONG NonBlock = 1;
if(ioctlsocket(ListenSocket, FIONBIO, &NonBlock) == SOCKET_ERROR){
printf("ioctlsocket() failed with error %d\n", WSAGetLastError());
return -1;
}
//为ListenSocket Socket创建对应的SOCKET_INFORMATION
//这样就可以把ListenSocket添加到SocketArray数组中
CreateSocketInformation(ListenSocket);
...
}
7. 获取就绪状态的套接字
浙公网安备 33010602011771号