最近重温一下windows的网络模型(windows网络与通信程序设计),发现大多数的select模型都会做一个遍历,然后处理对应的数据。里面提到了一点:(2)将fdSocket 集合的拷贝fdRead 传递给select 函数,当有事件发生时,select 函数移除fdRead 集合中没有未决I/O 操作的套接字句柄,然后返回。
值得注意的是:select 函数移除fdRead 集合中没有未决I/O 操作的套接字句柄 那么返回的fdRead集合就是需要处理的socket了,基于这一点那么就不必用这个循环for(int i=0; i<(int)fdSocket.fd_count; i++) 然后对比较原来fdSocket 集合与select 处理过的fdRead 集合 也就是这个比较就没有意义了if(FD_ISSET(fdSocket.fd_array[i], &fdRead))。
那么我们可以直接用 for (int i = 0; i < (int)fdRead.fd_count; i++) 来处理所有未决I/O 操作的套接字,可以大大提高效率。代码如下:
#include <iostream>
#include <string>
#include <map>
#include <winsock2.h>
using namespace std;
typedef struct stClientInfo
{
string cliAddr;
USHORT cliPort;
} ClientInfo;
typedef map<SOCKET,ClientInfo> ClientList;
ClientList g_CliList;
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{
//初始化 ws2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if (::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};
CInitSock theSock;
int main()
{
USHORT nPort = 4567; //服务器监听端口
SOCKET sListen =::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(nPort);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
//绑定到本机
if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
{
cout << "Fail bind()" << endl;
return -1;
}
//进入监听模式
if (::listen(sListen, 5) == 0)
{
cout << "Server Start OK!" << endl;
}
//select 模型处理过程
//1 初始化一个套接字集合 fdSocket ,将监听套接字添加进去
fd_set fdSocket;
FD_ZERO(&fdSocket);
FD_SET(sListen, &fdSocket);
char szBuf[1024]; //数据缓冲区
while (true)
{
//2 将fdSocket 集合的一个拷贝 fdRead 传递给 select 函数
//当有事件发生时,select 函数移除fdRead 集合中没有操作的套接字句柄,然后返回
fd_set fdRead = fdSocket;
int nRet = ::select(0, &fdRead, NULL, NULL, NULL);
if (nRet > 0)
{
//3 遍历 select 处理过的fdRead 集合并处理
for (int i = 0; i < (int)fdRead.fd_count; i++)
{
if (fdRead.fd_array[i] == sListen)
{
if (fdSocket.fd_count < FD_SETSIZE)
{
sockaddr_in addrRemote;
int nAddrLen = sizeof(addrRemote);
SOCKET sClient = ::accept(sListen, (SOCKADDR*)&addrRemote, &nAddrLen);
FD_SET(sClient, &fdSocket);
ClientInfo CilInfo;
CilInfo.cliAddr =inet_ntoa((addrRemote.sin_addr));
CilInfo.cliPort = ntohs(addrRemote.sin_port);
cout << "[+]Accept : " << CilInfo.cliAddr << ":" << CilInfo.cliPort << endl;
g_CliList.insert(make_pair(sClient,CilInfo));
}
else
{
cout << "[+]Too much connections!" << endl;
continue;
}
}
else
{
memset(szBuf, 0, 1024);
int nRecv =::recv(fdRead.fd_array[i], szBuf, 1024, 0);
if (nRecv > 0)
{
ClientList::iterator it=g_CliList.find(fdRead.fd_array[i]);
cout << "[+]Recv " <<it->second.cliAddr<<":"<<it->second.cliPort<<" :"<<szBuf << endl;
}
else//连接关闭、重启或者中断
{
::closesocket(fdRead.fd_array[i]);
FD_CLR(fdRead.fd_array[i], &fdSocket);
ClientList::iterator it=g_CliList.find(fdRead.fd_array[i]);
cout<<"[+]User "<<it->second.cliAddr<<":"<<it->second.cliPort<<" Closed."<<endl;
g_CliList.erase(it);
}
}
}
}
else
{
cout << "[-]Fail Select()" << endl;
break;
}
}
return 0;
}