• WinSockets编程(六)select模式


    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] = { '' };
    
        while (true)
        {
            FD_ZERO(&fdRead);
            for (int i = 0; i < g_nSockConn; i++)
            {
                FD_SET(g_Client[i].sockClient, &fdRead);
            }
    
            //只处理read事件,不过后面还是会有读写消息发送的
            nRet = select(0, &fdRead, NULL, NULL, &tv);
            std::cout << "g_nSockConn=" << g_nSockConn << std::endl;
            std::cout << "nRet=" << nRet << std::endl;
    
            if (nRet == -1)//没有连接或者没有读事件
            {
           Sleep(100);  //需睡眠
    continue; } for (int i = 0; i < g_nSockConn; i++) { std::cout << "已连接的套接字数目g_nSockConn=" << g_nSockConn << std::endl; Sleep(1000); if (FD_ISSET(g_Client[i].sockClient, &fdRead)) { nRet = recv(g_Client[i].sockClient, buf, sizeof(buf), 0); if (nRet == 0 || (nRet == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)) { std::cout << "Client " << inet_ntoa(g_Client[i].addrClient.sin_addr) << "closed" <<std::endl; closesocket(g_Client[i].sockClient); if (i < g_nSockConn - 1) { //将失效的sockClient剔除,用数组的最后一个补上去 g_Client[i--].sockClient = g_Client[--g_nSockConn].sockClient; } } else { std::cout <<"客户端地址为:"<<inet_ntoa(g_Client[i].addrClient.sin_addr) << ": " << std::endl; std::cout << buf <<std::endl; strcpy_s(buf, "Hello!"); nRet = send(g_Client[i].sockClient, buf, strlen(buf) + 1, 0); g_nSockConn--; } } } } return 0; }

     版本二(正常运行):

    #include <stdio.h>
    #include "initSocket.h"
    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)
        {
            printf(" Failed bind() 
    ");
            return -1;
        }
        ::listen(sListen, 5);
    
        fd_set fdSocket;
        FD_ZERO(&fdSocket);
        FD_SET(sListen, &fdSocket);
        while (TRUE)
        {
            fd_set fdRead = fdSocket;
            int nRet = ::select(0, &fdRead, NULL, NULL, NULL);
            if (nRet > 0)
            {
                for (int i = 0; i < (int)fdSocket.fd_count; i++)
                {
                    if (FD_ISSET(fdSocket.fd_array[i], &fdRead))
                    {
                        if (fdSocket.fd_array[i] == sListen)
                        {
                            if (fdSocket.fd_count < FD_SETSIZE)
                            {
                                sockaddr_in addrRemote;
                                int nAddrLen = sizeof(addrRemote);
                                SOCKET sNew = ::accept(sListen, (SOCKADDR*)&addrRemote, &nAddrLen);
                                FD_SET(sNew, &fdSocket);
                                printf("接收到连接(%s)
    ", ::inet_ntoa(addrRemote.sin_addr));
                            }
                            else
                            {
                                printf(" Too much connections! 
    ");
                                continue;
                            }
                        }
                        else
                        {
                            char szText[256];
                            int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
                            if (nRecv > 0)          //  (2)可读 
                            {
                                szText[nRecv] = '';
                                printf("接收到数据:%s 
    ", szText);
                            }
                            else
                            {
                                printf("连接断开...!");
                                ::closesocket(fdSocket.fd_array[i]);
                                FD_CLR(fdSocket.fd_array[i], &fdSocket);
                            }
                        }
                    }
                }
            }
            else
            {
                printf(" Failed select() 
    ");
                break;
            }
        }
        return 0;
    }
  • 相关阅读:
    系统吞吐量、TPS(QPS)、用户并发量、性能测试概念和公式(分享二十二)
    某云数据中心网络解决方案(分享二十一)
    oracle 12c 管理(分享二十)
    Codeforces 356D 倍增优化背包
    Codeforces 360D Levko and Sets (数论好题)
    gym/102253C Colorful Tree 树上计数
    Codeforces 360E 贪心 最短路
    Codeforces 360C DP 计算贡献
    Codeforces 354B 博弈, DP,记忆化搜索
    Codeforces 354C 暴力 数论
  • 原文地址:https://www.cnblogs.com/tinaluo/p/7698624.html
Copyright © 2020-2023  润新知