总体上来说select函数的作用:
确定一个或多个套接口的状态,本函数用于确定一个或多个套接口的状态,对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息,用fd_set结构来表示一组等待检查的套接口,在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足条件的套接口的数目。
简单来说select用来填充一组可用的socket句柄,当满足下列之一条件时:
1.可以读取的sockets。当这些socket被返回时,在这些socket上执行recv/accept等操作不会产生阻塞;
2.可以写入的sockets。当这些socket被返回时,在这些socket上执行send等不会产生阻塞;
3.返回有错误的sockets。
select的处理函数一般有:
同时和select配对使用的还有:
FD_CLR(s, *set)
Removes the descriptor s from set.
FD_ISSET(s, *set)
Nonzero if s is a member of the set. Otherwise, zero.
FD_SET(s, *set)
Adds descriptor s to set.
FD_ZERO(*set)
Initializes the set to the null set.
应该都知道,accept()函数是一个阻塞的函数,当有客户端请求连接服务端时才返回。如下面的例子
server端:
#include <iostream> #include <Windows.h> using namespace std; #define PORT 4000 #define IP_ADDRESS "127.0.0.1" #pragma comment(lib, "WS2_32.lib") int main(int argc, char* argv[]) { WSADATA Ws; SOCKET ServerSocket, CientSocket; struct sockaddr_in LocalAddr, ClientAddr; int Ret = 0; int AddrLen = 0; HANDLE hThread = NULL; //Init Windows Socket if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0) { cout << "Init Windows Socket Failed::" << GetLastError() << endl; return -1; } ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (ServerSocket == INVALID_SOCKET) { cout << "Create Socket Failed::" << GetLastError() << endl; return -1; } LocalAddr.sin_family = AF_INET; LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); LocalAddr.sin_port = htons(PORT); memset(LocalAddr.sin_zero, 0x00, 8); Ret = bind(ServerSocket, (struct sockaddr*)&LocalAddr, sizeof(LocalAddr)); if (Ret != 0) { cout << "Bind Socket Failed::" << GetLastError() << endl; return -1; } Ret = listen(ServerSocket, 10); if (Ret != 0) { cout << "listen Socket Failed::" << GetLastError() << endl; return -1; } cout << "服务端已经启动" << endl; char RecvBuffer[MAX_PATH]; while (true) { AddrLen = sizeof(ClientAddr); CientSocket = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &AddrLen);//阻塞的 if (CientSocket == INVALID_SOCKET) { cout << "Accept Failed::" << GetLastError() << endl; break; } cout << "客户端连接::" << inet_ntoa(ClientAddr.sin_addr) << ":" << ClientAddr.sin_port << endl; int Ret = 0; while (true) { memset(RecvBuffer, 0x00, sizeof(RecvBuffer)); Ret = recv(CientSocket, RecvBuffer, MAX_PATH, 0); if (Ret == 0 || Ret == SOCKET_ERROR) { cout << "客户端退出!" << endl; break; } cout << "接收到客户信息为:" << RecvBuffer << endl; } CloseHandle(hThread); } closesocket(ServerSocket); closesocket(CientSocket); WSACleanup(); return 0; }
client端:
#include <iostream> #include <Windows.h> #pragma comment(lib, "ws2_32.lib") using namespace std; #define PORT 4000 #define IP_ADDRESS "127.0.0.1" int main(int argc, char* argv[]) { WSADATA Ws; SOCKET CientSocket; struct sockaddr_in ServerAddr; int Ret = 0; int AddrLen = 0; HANDLE hThread = NULL; char SendBuffer[MAX_PATH]; //Init Windows Socket if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0) { cout << "Init Windows Socket Failed::" << GetLastError() << endl; return -1; } //Create Socket CientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (CientSocket == INVALID_SOCKET) { cout << "Create Socket Failed::" << GetLastError() << endl; return -1; } ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); ServerAddr.sin_port = htons(PORT); memset(ServerAddr.sin_zero, 0x00, 8); Ret = connect(CientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr)); if (Ret == SOCKET_ERROR) { cout << "Connect Error::" << GetLastError() << endl; return -1; } else { cout << "连接成功!" << endl; } while (true) { cin.getline(SendBuffer, sizeof(SendBuffer)); Ret = send(CientSocket, SendBuffer, (int)strlen(SendBuffer), 0); if (Ret == SOCKET_ERROR) { cout << "Send Info Error::" << GetLastError() << endl; break; } } closesocket(CientSocket); WSACleanup(); return 0; }上面的例子一个客户端只能接收处理一个客户端的请求!
加上select的应用,就能让一个server处理来自多个客户端的请求:
改写后的server端
#include <WinSock2.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib") #define PORT 4000 bool InitAndListern(SOCKET &sListen) { WSADATA wsaData; sockaddr_in local; WORD version = MAKEWORD(2, 0); int ret = WSAStartup(version, &wsaData); if (ret != 0) { printf("WSAStarup failed\n"); return 0; } local.sin_family = AF_INET; local.sin_addr.s_addr = INADDR_ANY; local.sin_port = htons((u_short)PORT); sListen = socket(AF_INET, SOCK_STREAM, 0); if (sListen == INVALID_SOCKET) { printf("Initial socket failed\n"); return 0; } if (bind(sListen, (sockaddr*)&local, sizeof(local)) != 0) { printf("Bind socket failed\n"); return 0; } if (listen(sListen, 10) != 0) { printf("Listen socket failed\n"); return 0; } return 1; } int main() { SOCKET sListen; if (InitAndListern(sListen) == 0) return 0; printf("Server wait for client connect...\n"); fd_set fdSocket; FD_ZERO(&fdSocket); FD_SET(sListen, &fdSocket);//将sListen添加进该集合 while (true) { fd_set fdRead = fdSocket; int nRet = select(NULL, &fdRead, NULL, NULL, NULL); if (nRet <= 0) break; for (int i = 0; i < (int)fdSocket.fd_count; ++i) { if (FD_ISSET(fdSocket.fd_array[i], &fdRead)) { if (fdSocket.fd_array[i] == sListen) { sockaddr_in addrRemote; int nAddrLen = sizeof(addrRemote); SOCKET sNew = ::accept(sListen, (sockaddr*)&addrRemote, &nAddrLen); FD_SET(sNew, &fdSocket); printf("Clietn %s connected\n", inet_ntoa(addrRemote.sin_addr)); } else { char buffer[1024]; memset(buffer, 0, 1024); int nRecev = recv(fdSocket.fd_array[i], buffer, 1024, 0); if (nRecev > 0) { printf("Received Client Msg:%s\n", buffer); send(fdSocket.fd_array[i], buffer, strlen(buffer), 0); } else { closesocket(fdSocket.fd_array[i]); FD_CLR(fdSocket.fd_array[i], &fdSocket); } } } } } return 0; }