Linux select网络模型

Posted 缄默先生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux select网络模型相关的知识,希望对你有一定的参考价值。

想比较Windows环境下的select,Linux真的是有点不省事,对于select调用之后的readfds,windwos可以直接获取大小并遍历,但是Linux却没有这么人性化,还需要自己添加一个数组,把所有连接服务器的客户端放进去,然后一个一个遍历。

////////////////////////////////////////////////////////////////////分割线//////////////////////////////////////////////////////////////////////////////////////////////////////////

select是用于I/O多路转接的一个系统调用函数。

在C程序中,该系统调用在 sys/select.h 或 unistd.h中声明,语法如下:

int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
参数描述
nfds sets的文件描述符的最大值
readfds fd_set 类型,包含了需要检查是否可读的描述符,输出时表示哪些描述符可读。可为 NULL
writefds fd_set 类型,包含了需要检查是否可写的描述符,输出时表示哪些描述符可写。可为 NULL
errorfds fd_set 类型,包含了需要检查是否出错的描述符,输出时表示哪些描述符出错。可为 NULL
timeout struct timeval 类型的结构体,表示等待检查完成的最长时间。

注:上述来自维基百科

////////////////////////////////////////////////////////////////////分割线//////////////////////////////////////////////////////////////////////////////////////////////////////////

对于select函数,在Linux环境下需要注意的是第一个参数nfds,因为在linux默认的文件描述符最大值为1024,请参看:

同时0,1,2分别被输出输入错误占用,所以在当新的客户端来的时候只能是从3开始往上增加。

同时nfds为所监听的所有文件描述符中,最大的文件描述符+1.

这点我有点疑惑,所以,如果你也有疑惑,请百度一下。

接下来就是我写的代码了,使用的是vector存储文件客户端的socket,但是我的代码可能有缺陷和漏洞,你可以参考并修补一下。

ps:由于qt中不能使用谷歌输入法,只能用ibus,所以我的注释没有中文,同时注释也很少。

//select

#include <sys/select.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>
#include <iostream>
#include <string.h>
using namespace std;

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 6663
#define CLIENT_NUM 100

int main(){

    int sockCli, sockSer;
    sockaddr_in addrCli, addrSer;
    vector<int> allsock;

     //init socket
    sockSer = socket(AF_INET,SOCK_STREAM,0);
    if(sockSer == -1){
        perror("socket init error:");
        return -1;
    }

    //init sockaddr_in
    addrSer.sin_port = htons(SERVER_PORT);
    addrSer.sin_family = AF_INET;
    inet_pton(AF_INET,SERVER_IP,&addrSer.sin_addr.s_addr);

    //bind
    if( -1 == bind(sockSer,(sockaddr*)&addrSer,sizeof(sockaddr)))
    {
        perror("bind error: ");
        return -1;
    }

    if(-1 == listen(sockSer,CLIENT_NUM)){
        perror("listen error: ");
        return -1;
    }

    //select
    fd_set readFds, tempReadfds;;
    FD_ZERO(&readFds);
    FD_SET(sockSer,&readFds);
    timeval time;
    time.tv_sec = 0;
    time.tv_usec = 3;

    while(1){

        FD_ZERO(&tempReadfds);
        memcpy(&tempReadfds,&readFds,sizeof(fd_set));


        int rect = select(allsock.size()+4,&tempReadfds,NULL,NULL,&time);
        if(rect == -1){
            perror("select error: ");
            return -1;
        }
        else if(rect == 0){
            continue;
        }
        else if(rect > 0){

            char buf[1024];
            bzero(buf,sizeof(buf));
            for(long unsigned int i = 0; i < allsock.size();i++){
                if(FD_ISSET(allsock[i],&tempReadfds)){
                    //client seng msg
                    int n = read(allsock[i],buf,1024);
                    if(n > 0){
                        cout << "client send msg " << buf << endl;
                        for(int i = 0; i < n; i++){
                            buf[i] = toupper(buf[i]);
                        }
                        write(allsock[i],buf,n);
                    }
                    else {
                       cout << "client exit" << endl;
                       FD_CLR(allsock[i],&readFds);
                       vector<int>::iterator it = allsock.begin()+i ;
                       allsock.erase(it);
                       i--;
                    }
                }
            }
            if(FD_ISSET(sockSer,&tempReadfds)){
                //new client connect
                socklen_t len = sizeof(sockaddr);
                sockCli = accept(sockSer,(sockaddr*)&addrCli,&len);
                if(sockCli == -1){
                    perror("accept error:");
                    continue;
                }
                else{
                    printf("new client\'s %s has connect, port: %d\\n",
                           inet_ntoa(addrCli.sin_addr),htons(addrCli.sin_port));
                    FD_SET(sockCli,&readFds);
                    FD_SET(sockCli,&tempReadfds);
                    allsock.push_back(sockCli);
                    memset(&addrCli,\'0\',sizeof(sockaddr));
                    sockCli = 0;
                }
            }
        }
    }
}

客户端请使用nc命令:

 

以上是关于Linux select网络模型的主要内容,如果未能解决你的问题,请参考以下文章

IO模型之二-linux网络IO模式select,poll,epoll

[Linux网络编程]多路IO复用Epoll Select问题补充

linux服务端的网络编程

Apache select和Nginx epoll模型区别

理解网络IO模型

linux下的网络I/O——转载