windows网络编程之select模型(改进)

       最近重温一下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;
}


猜你喜欢

转载自blog.csdn.net/kingzhang2000/article/details/76571400