poll实现IO复用,TCP通信

发布时间:2020-08-06 14:36:22 作者:小止1995
来源:网络 阅读:902

函数原型:

poll实现IO复用,TCP通信

函数说明:该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

参数说明:

poll实现IO复用,TCP通信

fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;

每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;

nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;

timeout:是poll函数阻塞的时间,单位:毫秒;

  1. 如果timeout==0,那么poll() 函数立即返回而不阻塞

  2. 如果timeout==INFTIM,即负数,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;

  3. poll()函数会以轮询方式在timeout所指定的毫秒时间长度之后返回

返回值:

>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;

==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒

-1: poll函数调用失败,同时会自动设置全局变量errno;

struct pollfd中event的设置参数:


poll实现IO复用,TCP通信

实现IO复用:关心输入输出条件就绪

#include<stdio.h>                                                               
#include<stdlib.h>
#include<string.h>
#include<poll.h>
int main()
{
    struct pollfd fds[2];
    fds[0].fd=0;
    fds[0].events=POLLIN;
    fds[0].revents=0;

    fds[1].fd=1;
    fds[1].events=POLLOUT;
    fds[1].revents=0;
    char buf[1024];
    ssize_t _s;
    int i=0;
    int timeout=5000;
    while(1)
    {
        timeout=5000;
        switch(poll(fds,2,timeout))
        {
            case -1://error
                perror("poll");
                break;
            case 0://timeout
                printf("time out\n");
                break;
            default:
            {
                for(i=0;i<2;++i)
                {
                    if(fds[i].fd==0&&fds[i].revents&POLLIN)
                    {
                        _s=read(0,buf,sizeof(buf)-1);
                        if(_s>0)
                        {
                            buf[_s]='\0';
                            if(strncmp(buf,"quit",4)==0)
                            {
                                close(fds[i].fd);
                                exit(0);
                            }
                            printf("echo:%s",buf);
                        } 
                        //fds[i].revents=0;//not need
                    }
                //  else if(fds[i].fd==1&&fds[i].revents&&POLLOUT)
                //  {
                //      strcpy(buf,"hello");
                //      printf("echo: %s",buf);
                //      fds[i].revents=0;//not need
                //  }
                }
            }
            break;
        }
    }
    return 0;
}

运行截图:

poll实现IO复用,TCP通信

TCP通信:监听socket

server:

  1. 创建监听套接字并初始化:调用socket,bind,listen,唯一描述符是监听描述符初始化数据结构。

  2. 阻塞于select:select等待某个事件发生或新客户连接的建立或是数据,FIN或RST的到达。

  3. accept新连接

     如果监听套接字变为可读,那么已建立一个新的连接,我们调用accept并更新相应数据结构。使用fds数组中第一个未用项记录这个已连接描述符。

  4. 检查现有连接

 对于每个现有客户连接,我们要测试其描述符是否在select返回描述符集中,如果是就从该客户读取一行文本,并回显,输出。如果该客户关闭了连接,那么read将返回0,更新数据结构。

poll与select不同在于描述符存储方式不同和参数类型不同。

1.结构体数组的管理:当每次有需要关心的描述符时,将其放入结构体中,每次有无效的描述符后,将其描述符置-1,下次poll函数会忽略它。当有新的描述符加入时,从头遍历结构体,将为-1的元素设为要关心的描述符事件状态。切记:当新的描述符加到结构体数组末尾时要更新关心描述符个数,即poll第二个参数。

2.每次调用poll后,结构体元素revents会存储就绪事件状态,当每次重新调用poll之前时,系统会自己设置其为0,重新监听关心事件(不需要用户重新置0)

3.poll中参数不是输入,输出型,因此timeout,struct pollfd *fds参数不需重置,nfds看情况(参照第一点),而select函数是输入输出类型,每次调用前需重置。

//server:最终版
include<stdio.h>                                                                        
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<poll.h>
#include<string.h>
#include<errno.h>
#define _BACKLOG_ 5
#define _SIZE_ 64
static void usage(const char* proc)
{
    printf("%s [ip][port]\n",proc);
}
static int start(char* ip,int port)
{
    //1.create a socket
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        exit(1);
    }
    //2.bind
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(port);
    local.sin_addr.s_addr=inet_addr(ip);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        perror("bind");
        exit(2);
    }
    //3.set listen  state
    if(listen(sock,_BACKLOG_)<0)
    {
        perror("listen");
        exit(3);
    }
    return sock;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {       
        usage(argv[0]);
        return 1;
    }
    int _port=atoi(argv[2]);
    char* _ip=argv[1];
    //listen_sock_fd
    int listen_sock=start(_ip,_port);
    struct  pollfd  polls[_SIZE_];//struct pollfd arrays
    int index=0;//effective fdnum
    int timeout=5000;//millseconds
    int i=0;//index
    polls[0].fd=listen_sock;
    polls[0].events=POLLIN;
    polls[0].revents=0;
    int max_num=1;
    for(i=1;i<_SIZE_;++i)
    {
        polls[i].fd=-1;
    }
    char buf[1024];
    ssize_t _s=0;
    struct sockaddr_in remote;//accept
    socklen_t len=sizeof(remote);                              
    while(1)
    {
        //new start
        //timeout=5000;
        switch(poll(polls,max_num,timeout))
        {
            case 0://timeout
                printf("time out...\n");
                break;
            case -1://error
                perror("poll");
                break;
            default://normal
            {
                for(i=0;i<max_num;++i)
                {
                    if(polls[i].fd==listen_sock&&(polls[i].revents&\
                        POLLIN))
                    {
                        printf("get a connect\n");
                        int new_sock=accept(listen_sock,\
                        (struct sockaddr*)&remote,&len);
                        if(new_sock<0)                    
                        {
                            perror("accept");
                            continue;
                        }
                        int j=1;//0 is listensock
                        for(;j<_SIZE_;++j)
                         {
                             if(polls[j].fd==-1)
                             {
                                 polls[j].fd=new_sock;
                                 polls[j].events=POLLIN;
                                 polls[j].revents=0;
                                 break;
                             }
                         }
                        if(j==_SIZE_)
                        {
                            printf("full");
                            close(new_sock);
                            return -1;
                        }
                        if(j==max_num)//加入的是max_num为下标的位置,即最后
                            max_num+=1;                   
                    }
                    else if(polls[i].fd>0&&(polls[i].revents&\
                        POLLIN))//read ready
                    {
                        _s=read(polls[i].fd,buf,sizeof(buf)-1);
                        if(_s>0)
                        {
                            buf[_s]='\0';
                            printf("client: %s",buf);
                            write(polls[i].fd,buf,strlen(buf));         
                            polls[i].revents=0;//not need
                        }
                        else if(_s==0)//client close
                        {
                            close(polls[i].fd);
                            polls[i].fd=-1;
                            printf("client is close\n");
                        }
                    }
                    else
                    {}
                }
            } 
            break;
        }
    }
    for(i=0;i<_SIZE_;++i)
    {
        if(polls[i].fd!=-1)
            close(polls[i].fd);
    }
    return 0;
}                                                                          
                                                              
//server:优质版
#include<stdio.h>                                                       
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<poll.h>
#include<string.h>
#include<errno.h>
#define _BACKLOG_ 5
#define _SIZE_ 64
static void usage(const char* proc)
{
    printf("%s [ip][port]\n",proc);
}
static int start(char* ip,int port)
{
    //1.create a socket
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        exit(1);
    }
    //2.bind
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(port);
    local.sin_addr.s_addr=inet_addr(ip);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        perror("bind");
        exit(2);
    }
    //3.set listen  state
    if(listen(sock,_BACKLOG_)<0)
    {
        perror("listen");
        exit(3);           
    }
    return sock;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        usage(argv[0]);
        return 1;
    }
    int _port=atoi(argv[2]);
    char* _ip=argv[1];
    //listen_sock_fd
    int listen_sock=start(_ip,_port);
    struct  pollfd  polls[_SIZE_];//struct pollfd arrays
    int index=0;//effective fdnum
    int timeout=5000;//millseconds
    int i=0;//index
    polls[0].fd=listen_sock;
    polls[0].events=POLLIN;
    polls[0].revents=0;
    ++index;
    for(i=1;i<_SIZE_;++i)
    {
        polls[i].fd=-1;    
    }
    char buf[1024];
    ssize_t _s=0;
    struct sockaddr_in remote;//accept
    socklen_t len=sizeof(remote);
    while(1)
    {
        //new start
        timeout=5000;
        int j=index;
        i=0;
        //消除struct pollfd中已不关心的描述符:前移,得到有效的最后一个元素下标
        while(i<j)
        {
            while(i<j&&polls[i].fd!=-1)//从前面数第一个无效的
                ++i;
            while(i<j&&polls[j].fd==-1)//从后面数第一个有效的
                --j;
            if(i<j)//复制fd
            {
                polls[i].fd=polls[j].fd;
                polls[i].events=POLLIN;//可不需要,因为event参数不发生变化,本程序只监听读事件
                polls[index].revents=0;
            }                             
        }
        index=i;
        //printf("%d",index);
        //须保证polls是有序的
        switch(poll(polls,index,timeout))//index表明最后一个关心的描述符在数组中下标+1=个数
        {
            case 0://timeout
                printf("time out...\n");
                break;
            case -1://error
                perror("poll");
                break;
            default://normal
            {
                for(i=0;i<index;++i)
                {
                    if(polls[i].fd==listen_sock&&(polls[i].revents&\
                        POLLIN))
                    {
                        printf("get a connect\n");
                        int new_sock=accept(listen_sock,\
                        (struct sockaddr*)&remote,&len);
                        if(new_sock<0)
                        {      
                            perror("accept");
                            continue;
                        }
                        if(index==_SIZE_)
                        {
                            printf("full");
                            close(new_sock);
                            return -1;
                        }
                        polls[index].fd=new_sock;
                        polls[index].events=POLLIN;
                        polls[index].revents=0;
                        ++index;
                    }
                    else if(polls[i].fd>0&&(polls[i].revents&\
                        POLLIN))//read ready
                    {
                        _s=read(polls[i].fd,buf,sizeof(buf)-1);
                        if(_s>0)
                        {
                            buf[_s]='\0';
                            printf("client: %s",buf);
                            write(polls[i].fd,buf,strlen(buf));   
                            polls[i].revents=0;
                        }
                        else if(_s==0)//client close
                        {
                            close(polls[i].fd);
                            polls[i].fd=-1;
                            printf("client is close\n");
                        }
                    }
                    else
                    {}
                }
            }
            break;
        }
    }
    for(i=0;i<_SIZE_;++i)
    {
        if(polls[i].fd!=-1)
            close(polls[i].fd);
    }
    return 0;
} 
//仿select版,用辅助数组存储,没有利用poll结构体的优点,event不清空,开销大                                                              
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        usage(argv[0]);
        return 1;
    }
    int _port=atoi(argv[2]);
    char* _ip=argv[1];
    //listen_sock_fd
    int listen_sock=start(_ip,_port);
    struct  pollfd  polls[_SIZE_];//struct pollfd arrays
    int index=0;//effective fdnum
    int timeout=5000;//millseconds
    int i=0;//index
    int fds[_SIZE_];
    fds[0]=listen_sock;  
    for(i=1;i<_SIZE_;++i)
    {
        fds[i]=-1;
    }
    char buf[1024];
    ssize_t _s=0;
    struct sockaddr_in remote;//accept
    socklen_t len=sizeof(remote);
    while(1)
    {
        index=0;//new start
        timeout=5000;
        for(i=0;i<_SIZE_;++i)
        {
            polls[i].fd=-1;
        }
        for(i=0;i<_SIZE_;++i)
        {
            if(fds[i]!=-1)
            {                  
                polls[index].fd=fds[i];
                polls[index].events=POLLIN;
                polls[index].revents=0;
                ++index;
            }
        }
        switch(poll(polls,index,timeout))
        {
            case 0://timeout
                printf("time out...\n");
                break;
            case -1://error
                perror("poll");
                break;
            default://normal
            {
                for(i=0;i<index;++i)
                {
                    if(polls[i].fd==listen_sock&&(polls[i].revents&\
                        POLLIN))         
                    {
                        printf("get a connect\n");
                        int new_sock=accept(listen_sock,\
                        (struct sockaddr*)&remote,&len);
                        if(new_sock<0)
                        {
                            perror("accept");
                            continue;
                        }
                        int j;
                        for(j=0;j<_SIZE_;++j)
                        {
                            if(fds[j]==-1)
                            {
                                fds[j]=new_sock;
                                break;
                            }
                        }
                        if(j==_SIZE_)
                        {                         
                            printf("full");
                            close(new_sock);
                            return -1;
                        }
                        polls[i].revents=0;//reset
                    }
                    else if(polls[i].fd>0&&(polls[i].revents&\
                        POLLIN))//read ready
                    {
                        _s=read(polls[i].fd,buf,sizeof(buf)-1);
                        if(_s>0)
                        {
                            buf[_s]='\0';
                            printf("client: %s",buf);
                            write(polls[i].fd,buf,strlen(buf));
                        }
                        else if(_s==0)//client close
                        {
                            close(polls[i].fd);
                            int j;                     
                            for(j=0;j<_SIZE_;++j)
                            {
                                if(fds[j]==polls[i].fd)
                                {
                                    fds[j]=-1;
                                    break;
                                }
                            }
                            printf("client is close\n");
                        }
                    }
                    else
                    {}
                }
            }
            break;
        }
    }
    for(i=0;i<_SIZE_;++i)
    {                           
            if(fds[i]!=-1)
            close(fds[i]);
    }
    return 0;
}          
//client:
#include<stdio.h>                                                       
#include<stdlib.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
void Usage(const char* proc)
{
    printf("%s [ip][port]",proc);
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    int client_sock=socket(AF_INET,SOCK_STREAM,0);
    if(client_sock<0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in client;
    client.sin_family=AF_INET;
    client.sin_port=htons(atoi(argv[2]));
    client.sin_addr.s_addr=inet_addr(argv[1]);
    char buf[1024];
    ssize_t _s;
    if(connect(client_sock,(struct sockaddr*)&client,sizeof(client))<0)
    {
        perror("connection");
        return 2;
    }
    while(1)
    {
        printf("please enter:\n");
        _s=read(0,buf,sizeof(buf)-1);        
        if(_s>0)
            buf[_s]='\0';
        if(strncmp(buf,"quit",4)==0)
        {
            printf("client is quit\n");
            break;
        }
        write(client_sock,buf,_s);
        _s=read(client_sock,buf,sizeof(buf)-1);
        if(_s>0)
        {
            buf[_s]='\0';
            printf("server->client: %s",buf);
        }
    }
    close(client_sock);

    return 0;
}

运行截图:

poll实现IO复用,TCP通信

推荐阅读:
  1. I/O多路复用——poll
  2. IO多路复用--select、 poll、 epoll的区别

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

poll

上一篇:PHP如何解决网站大流量与高并发的问题

下一篇:Spring security实现记录用户登录时间功能的案例分析

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》