IO多路复用的三种实现:select

时间:2022-08-23 01:19:53

select

 int select(int nfds, fd_set * readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

功能:多路IO复用。

参数:

 nfds: readfsd\ writefds\exceptfds三个位图中使用的最大的文件描述符号+1.

 readfds: 位图(128Byte,1024bit),需要监听的读事件集合

 writefds: 位图(128Byte,1024bit),需要监听的写事件集合

exceptfds: 位图(128Byte,1024bit),需要监听的异常事件集合

 timeout:超时时长,传NULL:如果没有监听的事件没有发生,就一直阻塞等待;传非0:表示超时时间,在规定的时间内如果监听的时间都没有发生,select 返回0;传0:表示非阻塞。调用的时候查看一遍监听集合,如果没有任何事件发生,select立即返回0。

返回值:

-1,出错并设置errno;0:表示没有任何监听的事发生;>0:监听的三个集合发生的事件和。

优点:

跨平台,开销小。

缺点:

受限数据结构的限制,能够监听的文件描述符上限1024个。

轮询监听机制,在活跃用户量小的时候监听效率比较低。

大量的用户和内核空间的数据拷贝。

利用select单进程实现多可客户端连接的服务器代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>                                                              
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<arpa/inet.h>
#include<sys/select.h>
#define SERPORT 8000
#define SERIP "IP地址"


int main(int argc, char* argv[])
{
    int lfd = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in seraddr,cliaddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERPORT);
    int dst;
    inet_pton(AF_INET,SERIP,(void*)&dst);
    seraddr.sin_addr.s_addr = dst;

    int ret = bind(lfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
    listen(lfd,64);

    socklen_t addrlen = sizeof(cliaddr);

    char buf[1024];
    int maxfd;
    fd_set rset, aset;
    FD_ZERO(&aset);
    FD_SET(lfd,&aset);
    maxfd = lfd;
    int i = 0;
    int cfd;
    char clip[32];
    while(1){
        rset = aset;
        int sret = select(maxfd + 1, &rset, NULL, NULL, NULL);
        if(sret<0){
            perror("select error");
            exit(1);
        }
        if(FD_ISSET(lfd, &rset)){
            cfd = accept(lfd,(struct sockaddr*)&cliaddr,&addrlen);
            if(cfd<0){
                perror("accept error");
                exit(1);
            }
            //网络字节序整形IP地址转化成一个本地字节序点分十进制的字符窜IP
            inet_ntop(AF_INET,&cliaddr.sin_addr,clip,sizeof(clip));
            printf("clien IP=%s,PORT=%d connect ok\n",clip,ntohs(cliaddr.sin_port));
            FD_SET(cfd, &aset);
            if(cfd>maxfd){
                maxfd = cfd;
            }
            if(--sret ==0 ){
                continue;
            }
        }
        for(i = lfd+1;i<maxfd+1;i++){
            if(FD_ISSET(i, &rset)){
            int rr = read(i,buf,sizeof(buf));
            if(rr < 0){
                perror("read error");
                exit(1);
            }
            else if(rr == 0){
                //客户端断开连接
                FD_CLR(i, &aset);
                close(i);
                printf("客户端断开连接\n");
            }
            write(STDOUT_FILENO,buf,rr);
            write(i,buf,rr);
            if(--sret == 0){
                break;
            }
         }
      }
     }   
    return 0;
}