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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/renchunlin66/article/details/52075844

        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模型设计海量级连接服务器

扫描二维码关注公众号,回复: 3832266 查看本文章

注: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_ 10
int 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_in
struct 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 epoll
int epfd=epoll_create(256);
if(epfd<0)
{
    perror("epoll_create");
    exit(5);
    }//error
else
{
   struct epoll_event _ev;
   _ev.events=EPOLLIN;
   _ev.data.fd=listen_sock;
  epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&_ev);//add a socket

struct epoll_event _ready_ev[128];
int _ready_evs=128;
int _timeout=-1;//block
int 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 






猜你喜欢

转载自blog.csdn.net/renchunlin66/article/details/52075844
今日推荐