I/O多路转接之poll

poll()函数:这个函数是某些Linux系统提供的用于执行与select()函数同等功能的函数,下面是这个函数的声明:

#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);

参数说明:
fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;

返回值:

0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
-1: poll函数调用失败,同时会自动设置全局变量errno;
poll()接受一个指向结构struct pollfd列表的指针,其中包括了你想监测的文件描述符和事件,结构体如下:

struct pollfd {
int fd; /*文件描述符*/
short events; /* 等待的需要测试事件 */
short revents; /* 实际发生了的事件,也就是返回结果 */
};

poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制, 
  pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。
  poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。 
  poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。

poll的优点:
1)poll() 不要求开发者计算最大文件描述符加一的大小。
2)poll() 在应付大数目的文件描述符的时候相比于select速度更快
3)它没有最大连接数的限制,原因是它是基于链表来存储的。
poll的缺点:
1)大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
2)与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符

利用poll编写基于TCP的服务器:
服务器端代码:

#include<stdio.h>
#include<poll.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
static void use(char* proc)
{
    printf("use:%s [local_ip][local_port]",proc);
}

int startup(const char* ip,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 2;
    }
    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    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");
        return 3;
    }
    if(listen(sock,10)<0)
    {
        perror("listen");
        return 4;
    }
    return sock;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        use(argv[0]);
        return 1;
    }
    int listen_sock = startup(argv[1],atoi(argv[2]));
    struct pollfd _poll[64];
    _poll[0].fd = listen_sock;
    _poll[0].events = POLLIN;
    _poll[0].revents = 0;
    //int poll_len = sizeof(_poll)/sizeof(_poll[0]);

    int i = 1;
    for(;i<64;i++)
    {
        _poll[i].fd = -1;
    }
    int maxfd = 1;
while(1)
{
    int timeout = 5000;
    switch( poll(_poll,maxfd,timeout))
    {
        case -1:
            perror("poll");
            break;
        case 0:
            printf("timeout\n");
            break;
        default:
            {
                int i = 0;
                for(;i<maxfd;i++)
                {
                    if(_poll[i].fd==listen_sock&&_poll[i].revents&POLLIN)
                    {
                    struct sockaddr_in client;
          socklen_t len = sizeof(client);
                        int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
                        if(new_sock<0)
                        {
                            perror("accept");
                            continue;
                        }
                        printf("get a new client:%s:%d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));
                        int j =1;
                        for(;j<64;j++)
                        {
                            if(_poll[j].fd==-1)
                            {
                            _poll[j].fd = new_sock;
                            _poll[j].events = POLLIN;
                            _poll[j].revents = 0;
                            break;
                            }
                        }
                        if(j==64)
                        {
                            close(new_sock);
                        }
                        if(j==maxfd)
                        {
                            maxfd++;
                        }
                    }//if
                    else if(_poll[i].fd != listen_sock && _poll[i].revents & POLLIN)
                    {
                        char buf[1024];
                        ssize_t s = read(_poll[i].fd,buf,sizeof(buf)-1);
                        if(s>0)
                        {
                            buf[s] = 0;
                            printf("client say#%s\n",buf);
                        }
                        else if(s<0)
                        {

                            perror("read");
                            close(_poll[i].fd);
                            _poll[i].fd = -1;

                        }
                        else if(s==0)
                        {
                            printf("client quit!\n");
                            close(_poll[i].fd);
                            _poll[i].fd = -1;
                        }
                    }
                }//for
            }
            break;  
    }
}
    for(i=0;i<64;++i)
    {
        if(_poll[i].fd!=-1)
            close(_poll[i].fd);
    }
    return 0;
}

客户端代码:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
static void Use(const char* proc)
{
    printf("%s [server_ip] [server_port]\n",proc);
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Use(argv[0]);
        return 1;
    }
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 2;
    }
    struct sockaddr_in peer;
    peer.sin_family = AF_INET;
    peer.sin_port = htons(atoi(argv[2]));
    peer.sin_addr.s_addr = inet_addr(argv[1]);
    if(connect(sock,(struct sockaddr*)&peer,sizeof(peer))<0)
    {
        perror("connect");
        return 3;
    }
    char buf[1024];
    while(1)
    {
        printf("please enter#");
        fflush(stdout);
        ssize_t s = read(0,buf,sizeof(buf));
        if(s<0)
        {
            perror("read");
            return 4;
        }
        buf[s-1]=0;
        write(sock,buf,strlen(buf));
        printf("server echo#%s\n",buf);
    }
    close(sock);
    return 0;
}

运行结果示例:
这里写图片描述
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值