关于TCP ,select,epoll服务器的区别与联系

来源:互联网 发布:node express restful 编辑:程序博客网 时间:2024/05/20 05:29

        select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。而TCP服务器要想实现多个描述符的等待需要用多进程多线程的方式实现。IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:

(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。

(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。

(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。

(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。

(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
       与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

       下面重点对比select服务器和epoll服务器:

(一)select服务器原理图。


(二)epoll服务器模型

epoll模型设计海量级连接服务器

注:select 原理图,摘自 IBM iSeries 信息中心

(三)优缺点对比

select的几大缺点:


(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大


(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大


(3)select支持的文件描述符数量太小了,默认是1024点

epoll是select的改进其优势:

        对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。
  对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。
  对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大

如下是源码:   

(1)TCP服务器

server.c

# include<errno.h># include<unistd.h># include<string.h># include<sys/types.h># define _PORT_ 999# define BACKLOG_ 10int main (){    int sock=socket(AF_INET,SOCK_STREAM,0);   if(sock<0)    {       printf("create socket error,error is:%d,errstring is:%d\n",errno,strerror(errno));       }       struct sockaddr_in server_socket;       struct sockaddr_in client_socket;       bzero(&server_socket,sizeof(server_socket));       server_socket.sin_family=AF_INET;       server_socket.sin_addr.s_addr=htonl(INADDR_ANY);        server_socket.sin_addr.port=htons(_PORT_);       if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0);       {            printf("error code is:%d,errstring is:%d\n",errno,strerror(errno));            close(sock);            return 1;           }                      if(listen(sock,_BACKLOG_)<0)           {                        printf("listen error,error is:%d,errstring is:%d\n",errno,strerror(errno));            close(sock);            return 2;                }               printf("bind and listen success,wait accept..");               for(::)               {                   socklen_t len=0;                   int client_sock=accept(sock,(struct sockaddr *)&client_socket,&len);                   if(client_sock<0)                   {                   printf("accept error   error code :%d, error string :%d\n",errno,strerror(errno));                   close(sock);                   return 3;                   }                   char buf_ip[INET_ADDRSTRLEN];                   memset(buf_ip,'\0',sizeof(buf_ip));                   inet_ntop(AF_INET,&client_socket.sin_addr,buf_ip,sizeof(buf_ip));                   printf("get connect, ip is:%d,port is:%d\n",buf_ip,ntohs(client_socket.sin_port));                   while(1)                   {                       char buf[1024];                       memset(buf,'\0',sizeof(buf));                       read(client_sock,buf,sizeof(buf));                       printf("client :# %s\n",buf);                       printf("server:$");                       memset(buf,'\0',sizeof(buf));                       fgets(buf,sizeof(buf),stdin);                       buf[strlen(buf)-1]='\0';                       write(client_sock,buf,strlen(buf)+1);                       printf("please wait..");                       }               }               close(sock);               return 0;    }
client.c

# include<stdio.h># include<unistd.h># include<sys/socket.h># include<sys/types.h># include<strings.h># include<error.h># include< netinet/in.h># include<arpa/inet.h># define SERVER_PORT 9999# define SERVER_IP "1992.168.0.111"int main(int argc,char* argv[]){    if(argc!=2)    {        printf("Usage:client IP\n");        return 1;        }        char *str=argv[1];        char buf[1024];        memset(buf,'\0',sizeof(buf));        struct sockaddr_in server_sock;        int sock=socket(AF_INET,SOCK_STREAM,0);        bzero(&server_sock,szieof(server_sock));        server_sock.sin_family=AF_INET;        inet_pton(AF_INET,SERVER_IP,&server_sock.sin_addr);        server_sock.sin_port=htons(SERVER_PORT);        int ret=connect(sock,(struct sockaddr *)&server_sock,sizeof(server_sock));        if(ret<0)        {            printf("connect is failed ...error is :%d,error string is:%d\n",errno,strerror(errno));            return 1;            }            printf("connect success\n")            while(1)            {                printf("client:#");                fgets(buf,sizeof(buf),stdin);                buf[strlen(buf)-1]='\0';                write(sock,buf,sizeof(buf));                if(strncasecmp(buf,"quit",4)==0)                {                    printf("quit!\n");                    break;                    }                    printf("please wait..\n");                    read(sock,buf,sizeof(buf));                    printf("server:$%s\n",buf);                }                close(sock);                return 0;    }
(2)select服务器

# include<stdio.h># include<string.h># include<stdlib.h># include<sys/socket.h># include<sys/types.h># include<sys/select.h># include<netinet/in.h>//not clear#define _PORT_ 8080#define _MAX_SIZE_ 10#define _BACK_LOG_ 3#define _BUF_SIZE_ 1024 int fd_arr[_MAX_SIZE_]; int max_fd=0; static void init_fd_arr() {     int i=0;     for(;i<_MAX_SIZE_;i++)     {        fd_arr[i]=-1;         }     }static int add_fd_arr(int fd){    int i=0;    for(;i<_MAX_SIZE_;i++)    {        if(fd_arr[i]==-1)        {            fd_arr[i]=fd;            return 0;            }        }        return 1;    }    static int remove_fd_arr(int fd)    {        int i=0;        for(;i<_MAX_SIZE_;i++)        {            if(fd_arr[i]==fd)            {                fd_arr[i]=-1;//remove target fd                break;                }            }            return 0;        }static int reload_fd_set(fd_set *fd_setp) //not clear{    int i=0;    for(;i<_MAX_SIZE_;i++)    {        if(fd_arr[i]!=-1)        {            FD_SET(fd_arr[i],fd_setp);            if(fd_arr[i]>max_fd)            {                max_fd=fd_arr[i];                }            }        }        return 0;    }    static void print_msg(int i,char buf[])    {        printf("fd:%d,msg:%s\n",i,buf);        }    int select_server()    {        struct sockaddr_in ser;        struct sockaddr_in cli;        fd_set fds;                int fd=socket(AF_INET,SOCK_STREAM,0);        if(fd<0)        {            perror("create socket error");            return 1;            }            printf("create socket success\n");            int yes=1;//not clear            setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int));                       memset(&ser,'\0',sizeof(ser));            ser.sin_family=AF_INET;            ser.sin_port=htons(_PORT_);            ser.sin_addr.s_addr=INADDR_ANY;//auto fill with IP            if(bind(fd,(struct sockaddr*)&ser,sizeof(ser))<0)            {                perror("bind error");                return 2;                }                printf("bind socket success\n");                init_fd_arr();                add_fd_arr(fd);                FD_ZERO(&fds);                if(listen(fd,_BACK_LOG_)<0)                {                    perror("listen error");                    return 3;                    }                    printf("listen socket success\n");                    while(1)                    {                        #ifdef _SELECT_                        reload_fd_set(&fds);                        struct timeval timeout=(30,0);                        switch(select(max_fd+1,&fds,NULL,NULL,&timeout))                        {                            case -1:                             printf("select error,quit!\n");                             exit(1);                             break;                            case 0:                              printf("select timeout,continue wait..\n");                              break:                            default://return normal                              {                                  int index=0;                                  for(;index<_MAX_SIZE_;index++)                                  {                if(index==0&&fd_arr[index]!=-1&&FD_ISSELECT(fd_arr[index],&fds))//new accept                               socklen_t len=sizeof(cli);                               memset(&cli,'\0',sizeof(cli));                               int new_fd=accept(fd,(struct sockaddr*)&cli,&len);                               if(-1!=new_fd)                               {                                   printf("get a new requeset!\n");                                   if(1==add_fd_arr(new_fd))//add new fd failed                                   {                                       perror("fd arr is full.close fd !\n")                                       close(new_fd);                                       }                                   }                                   continue;                                      }                     if(fd_arr[index]!=-1&&FD_ISSET(fd_arr[index],&fds))//just for read fd                                      {                                          char buf[_BUF_SIZE_];                                          memset(buf,'\0',sizeof(buf));                      ssize_t size=recv(fd_arr[index],buf,sizeof(buf)-1,0);//read data                                          if(size==0||size==-1)                                          {                     printf("remote client close. size is:,%d\n",size);                                              remove_fd_arr(fd_arr[index]);                                              close(fd_arr[index]);                                              FD_CLR(fd_arr[index],&fds);                                              }                                              else                                              {                                                  print_msg(index,buf);                                                  }                                         }                                  }                            }             break;                        }   #endif                                  }    }        int main ()        {            select_server();            return 0;            }
(3)epoll服务器

# include<stdio.h># include<stdlib.h># include<fcntl.h># include<string.h># include<unistd.h># include<sys/socket.h># include<netinet/in.h># include<arpa/inet.h># include<sys/epoll.h>static int startup(const char *_ip,int _port)//create a socket{//sock()int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){    perror("socket error");    exit(2);    }//struct sockaddr_instruct sockaddr_in local;local.sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=inet_addr(_ip);//bind()if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)    {        perror("bind error");        exit(3);        }//listen()if(listen(sock,5)<0){    perror("listen error");    exit(4);    }    return sock;}static void usage(const char *proc){    printf("Usage:%s [ip] [port]",proc);    }static int set_noblock(int sock){    int fl=fcntl(sock,F_GETFL);    return fcntl(sock,F_SETFL,fl|O_NONBLOCK);    }int main (int argc,char *argv[]){   // if()   if(argc!=3)   {       usage("argv[0]");       exit(1);       }int listen_sock=startup(argv[1],atoi(argv[2]));//create a epollint epfd=epoll_create(256);if(epfd<0){    perror("epoll_create");    exit(5);    }//errorelse{   struct epoll_event _ev;   _ev.events=EPOLLIN;   _ev.data.fd=listen_sock;  epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&_ev);//add a socketstruct epoll_event _ready_ev[128];int _ready_evs=128;int _timeout=-1;//blockint nums=0;int done=0;while(!done){    switch(nums=(epoll_wait(epfd,_ready_ev,_ready_evs,_timeout)))    {        case 0:        printf("time out...\n");        break;        case -1:        perror("epoll_wait");        break;        //default        default:     {        int i=0;        for(;i<nums;++i)        {            int _fd=_ready_ev[i].data.fd;            if(_fd==listen_sock&&_ready_ev[i].events&EPOLLIN)            {                //get a new link                                struct sockaddr_in peer;                socklen_t len=sizeof(peer);                int new_sock=accept(listen_sock,(struct sockaddr*)&peer,&len);//need finish                if(new_sock>0)                {                    printf("client info,socket:%s:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));                    //link success                    //then add to epoll                    _ev.events=EPOLLIN | EPOLLET;                    _ev.data.fd=new_sock;                    set_noblock(new_sock);                    epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&_ev);                    }                    else                    {                        if(_ready_ev[i].events&EPOLLIN)                        {                            char buf[102400];                            memset(buf,'\0',sizeof(buf));                            //read  or  write                             ssize_t _s=recv(_fd,buf,sizeof(buf)-1,0);                             if(_s>0)                             {                                 printf("client#%s\n",buf);                                _ev.events=EPOLLOUT | EPOLLET;                                 _ev.data.fd=_fd;                                 epoll_ctl(epfd,EPOLL_CTL_MOD,_fd,&_ev);                                 }                                 else if(_s==0)                                 {                                     printf("client close..\n");                                     //close server                                     //close epoll                                     epoll_ctl(epfd,EPOLL_CTL_DEL,_fd,NULL);                                                      close(_fd);                                     }                                     else                                     {                                         perror("error");                                         }                            }                            else if(_ready_ev[i].events&EPOLLOUT)                            {                                const char *msg="HTTP/1.1 200 OK\r\n\r\n<h1>hello world +_+</h1>\r\n";                                send(_fd,msg,strlen(msg),0);                                epoll_ctl(epfd,EPOLL_CTL_DEL,_fd,NULL);                                close(_fd);                                }                        }                }            }            break;     }        }    } }}//exc 1. the diference of select poll and epoll//2.epoll// http xieyi(zhengdehenjindian)kanzhegewangye 






0 0
原创粉丝点击