select模式的思想
创建FD_SET fd_all,并初始化FD_ZERO(&fd_all);
Step1 初始时:
Step2 加入一个套接字之后,比如FD_SET(sServer,&fd_all);
Step3 调用select函数之后,有两种情况
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
上述的select假设是在非阻塞的情况下。由图示可以看出, select对FD_SET的结构进行了动态改变,没有变化的会置为0,有变化的会保持为1,这就是select的思想。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
保持FD_SET的状态
假设有1,2,3,4四个标号的套接字加入FD_SET
//伪码 FD_SET(1,&fd_all); FD_SET(2,&fd_all); FD_SET(3,&fd_all); FD_SET(4,&fd_all); for(;;) { ... select(0,&fd_all,0,NULL,NULL); ... }
在进行选择之后,有些套接字可能暂时没有数据收发就被select函数过滤掉了,所以要保持套接字的状态,模式如下:
//保持套接字状态的方法示例 while(true) { fd_read=fd_all; fd_write=fd_all; select(0,&fd_read,0,NULL,NULL);//①阻塞; for(UINT i=0;i<fd_all.fd_count;i++) { if(FD_ISSET(fd_all.fd_array[i],&fd_read)) { if(fd_all.fd_array[i]==sServer)//② { sockaddr_in addrClient; int addrClientlen = sizeof(addrClient); sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen);//③ FD_SET(sClient,&fd_all);//④ } else //⑤ 收发数据 { ZeroMemory(buf, BUF_SZIE); retVal = recv(fd_all.fd_array[i], buf, BUF_SZIE-1, 0);//⑥ if (SOCKET_ERROR == retVal) { printf("recv failed! "); closesocket(sServer); closesocket(sClient); //关闭套接字 WSACleanup(); return -1; } printf("%s ", buf); } } }
版本一(有点问题,需要修正):
/**************************************************************************************************************
2018/10/9号进行修正:
原因:
1. 在select轮询时,如果没有连接返回-1
2. 在while循环时,需要Sleep(100),不然CPU空转太厉害导致CPU使用率上升!
/**************************************************************************************************************
#include <WinSock2.h> #include <iostream> #include <stdio.h> #pragma comment(lib,"ws2_32.lib") #define PORT 8000 #define MSGSIZE 255 #define SRV_IP "127.0.0.1" int g_nSockConn = 0; //请求连接的数目 struct ClientInfo { SOCKET sockClient; //客户端套接字 SOCKADDR_IN addrClient; //客户端地址 }; ClientInfo g_Client[FD_SETSIZE]; //客户端套接字集合; DWORD WINAPI WorkThread(LPVOID lpParameter); int main(int argc, char *argv[]) { WSADATA wsd; WSAStartup(MAKEWORD(2, 2), &wsd); SOCKET sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//创建套接字 SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = inet_addr(SRV_IP); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(PORT); bind(sockListen, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//绑定 listen(sockListen, 64);//监听 DWORD dwThreadIDRecv = 0; DWORD dwThreadIDWrite = 0; HANDLE hand = CreateThread(NULL, 0, WorkThread, NULL, 0, &dwThreadIDRecv);//工作线程 if (hand == NULL) { std::cout <<"创建线程失败!"<<std::endl; return -1; } SOCKET sockClient; SOCKADDR_IN addrClient; int nLenAddrClient = sizeof(SOCKADDR_IN); while (true) { sockClient = accept(sockListen, (SOCKADDR*)&addrClient, &nLenAddrClient); if (sockClient != INVALID_SOCKET) { g_Client[g_nSockConn].addrClient = addrClient;//保存客户端地址信息 g_Client[g_nSockConn].sockClient = sockClient; g_nSockConn++; } } closesocket(sockListen); WSACleanup(); return 0; } DWORD WINAPI WorkThread(LPVOID lpParameter) { std::cout << "线程开始执行...." << std::endl; FD_SET fdRead; int nRet = 0; //记录发送或者接受的字节数 TIMEVAL tv; //设置超时等待时间 tv.tv_sec = 1; tv.tv_usec = 0; char buf[MSGSIZE] = { '