zoukankan      html  css  js  c++  java
  • socket编程之select()

    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

    参数列表:
    int maxfdp,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,在Windows中这个参数的值无所谓。   
    fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。   
    fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。   
    fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
    struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态:
    第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
    第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
    第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
    返回值:
    负值:select错误
    正值:某些文件可读写或出错
    0:等待超时,没有可读写或错误的文件

    readfds:
    If listen has been called and a connection is pending, accept will succeed.
    Data is available for reading (includes OOB data if SO_OOBINLINE is enabled).
    Connection has been closed/reset/terminated.

    writefds:
    If processing a connect call (nonblocking), connection has succeeded.
    Data can be sent.

    exceptfds:
    If processing a connect call (nonblocking), connection attempt failed.
    OOB data is available for reading (only if SO_OOBINLINE is disabled).

    FD_CLR(s, *set):从set中删除套接字s。
    FD_ISSET(s, *set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
    FD_SET(s, *set):将套接字s加入集合set。
    FD_ZERO( * set):将set初始化成空集合。

    在大规模的网络连接方面,还是推荐使用IOCP或EPOLL模型.但是Select模型可以使用在诸如对战类游戏上,比如类似星际这种,因为它小巧易于实现,且对战类游戏的网络连接量并不大. 对于Select模型想要突破Windows 64个限制的话,可以采取分段轮询,一次轮询64个.例如套接字列表为128个,在第一次轮询时,将前64个放入队列中用Select进行状态查询, 待本次操作全部结束后.将后64个再加入轮询队列中进行轮询处理.这样处理需要在非阻塞式下工作.以此类推,Select也能支持无限多个。

    简单例子:

    
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    #include <WinSock2.h>
    #include <iostream>
    #include<Windows.h>
    #pragma comment(lib, "ws2_32.lib") 
    
    
    using namespace std;
    
    SOCKET server, clientSet[FD_SETSIZE];
    int serverAddLen;//服务器地址长度
    sockaddr_in serverAdd;//服务器地址
    int clientCount = 0;//记录接入客户端的数量
    
    DWORD WINAPI WorkerThread(LPVOID lpParam);
    
    int main()
    {
    
        WSADATA wsaData;
        SOCKET client;
        sockaddr_in clientAdd;
        int clientAddLen = sizeof(clientAdd);
        DWORD       dwThreadId;
    
        DWORD ret = MAKEWORD(2, 2);
        if (ret == WSAStartup(0x0202, &wsaData))
        {
            cerr << "初始化失败!" << endl;
        }
    
        serverAdd.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
        serverAdd.sin_family = AF_INET;
        serverAdd.sin_port = htons(5000);
        serverAddLen = sizeof(serverAdd);
        server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        bind(server, (struct sockaddr*)&serverAdd, serverAddLen);
        listen(server, SOMAXCONN);
        CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
    
    
        while (clientCount < FD_SETSIZE)
        {
            client = accept(server, (struct sockaddr *)&clientAdd, &clientAddLen);
            cout << "Accepted client:" << inet_ntoa(clientAdd.sin_addr) << ":" << ntohs(clientAdd.sin_port);
            clientSet[clientCount++] = client;
        }
    
        WSACleanup();
        system("pause");
        return 1;
    
    }
    
    DWORD WINAPI WorkerThread(LPVOID lpParam)
    {
        fd_set fdread;
        int ret;
        char szMesage[8192];
    
    
        while (1)
        {
            int i;
            FD_ZERO(&fdread);
            for (i = 0; i < clientCount; ++i)
            {
                if (clientSet[i] != NULL)
                    FD_SET(clientSet[i], &fdread);
            }
            timeval timeout = { 3,0 };
            ret = select(i + 1, &fdread, NULL, NULL, &timeout);
            if (ret == 0) //超时
            {
                continue;
            }
            for (i = 0; i < clientCount; ++i)
            {
                if (FD_ISSET(clientSet[i], &fdread))
                {
                    ret = recv(clientSet[i], szMesage, sizeof(szMesage), 0);
                    if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
                    {
                        cerr << "client socket closed" << endl;
                        FD_CLR(clientSet[i], &fdread);
                        closesocket(clientSet[i]);
                        clientSet[i] = NULL;
                    }
                    else
                    {
                        szMesage[ret] = '';
                        cout << szMesage << endl;
                    }
                }
            }
        }
        return 0;
    }

    改进:

    
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    #include <WinSock2.h>
    #include <iostream>
    #include<Windows.h>
    #pragma comment(lib, "ws2_32.lib") 
    
    
    using namespace std;
    
    SOCKET server, clientSet[FD_SETSIZE];
    int serverAddLen;//服务器地址长度
    sockaddr_in serverAdd;//服务器地址
    int clientCount = 0;//记录接入客户端的数量
    
    void WorkerThread();
    
    int main()
    {
    
        WSADATA wsaData;
        DWORD       dwThreadId;
    
        DWORD ret = MAKEWORD(2, 2);
        if (ret == WSAStartup(0x0202, &wsaData))
        {
            cerr << "初始化失败!" << endl;
        }
    
        serverAdd.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
        serverAdd.sin_family = AF_INET;
        serverAdd.sin_port = htons(5000);
        serverAddLen = sizeof(serverAdd);
        server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        bind(server, (struct sockaddr*)&serverAdd, serverAddLen);
        listen(server, SOMAXCONN);
        WorkerThread();
    
    
        /*while (clientCount < FD_SETSIZE)
        {
            client = accept(server, (struct sockaddr *)&clientAdd, &clientAddLen);
            cout << "Accepted client:" << inet_ntoa(clientAdd.sin_addr) << ":" << ntohs(clientAdd.sin_port)<<endl;
            clientSet[clientCount++] = client;
        }*/
    
        WSACleanup();
        system("pause");
        return 1;
    
    }
    
    void WorkerThread()
    {
        fd_set fdread;
        int ret;
        char szMesage[8192];
    
        SOCKET client;
        sockaddr_in clientAdd;
        int clientAddLen = sizeof(clientAdd);
    
    
        while (1)
        {
            int i;
            FD_ZERO(&fdread);
            FD_SET(server, &fdread);
            for (i = 0; i < clientCount; ++i)
            {
                if (clientSet[i] != NULL)
                    FD_SET(clientSet[i], &fdread);
            }
            timeval timeout = { 3,0 };
            ret = select(i + 1, &fdread, NULL, NULL, &timeout);
            if (ret == 0) //超时
            {
                continue;
            }
            else if(ret== SOCKET_ERROR&&GetLastError()!= WSAEINVAL)//WSAEINVAL参数全为空错误
            {
                cerr << "select error!" <<GetLastError()<< endl;
                break;
            }
    
            if (FD_ISSET(server, &fdread))
            {
                if (clientCount < FD_SETSIZE)
                {
                    cout << "有新的连接请求!" << endl;
                    client = accept(server, (sockaddr*)&clientAdd, &clientAddLen);
                    if (client == INVALID_SOCKET)
                    {
                        cerr << "无法接受请求" << endl;
                    }
                    else
                    {
                        cout << "Accepted client:" << inet_ntoa(clientAdd.sin_addr) << ":" << ntohs(clientAdd.sin_port) << endl;
                        clientSet[clientCount++] = client;
                    }
                }
                else
                {
                    cerr << "达到上限无法接受请求!" << endl;
                }
    
    
            }
    
    
            for (i = 0; i < clientCount; ++i)
            {
                if (FD_ISSET(clientSet[i], &fdread))
                {
                    ret = recv(clientSet[i], szMesage, sizeof(szMesage), 0);
                    if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
                    {
                        cerr << "client socket closed" << endl;
                        FD_CLR(clientSet[i], &fdread);
                        closesocket(clientSet[i]);
                        clientSet[i] = NULL;
                    }
                    else
                    {
                        szMesage[ret] = '';
                        cout << szMesage << endl;
                    }
                }
            }
        }
    
    }
  • 相关阅读:
    mysql 用户管理 权限控制
    微信开发--自动回复图片
    MySQL要导出成excel的方法
    mysql 和excel相互转换
    MYSQL 函数复习
    查找算法
    PySpider的安装
    在Windows下安装scrapy
    chromedriver安装
    Sublime Text 3中配置Python3的开发环境
  • 原文地址:https://www.cnblogs.com/ggzone/p/4429846.html
Copyright © 2011-2022 走看看