poll服务器

poll是I/O复用多路转接的另一种方法,其优化了select两个缺点:

  1. poll服务器支持的文件描述符数目没有上限;
  2. poll服务器函数接口的参数与select不同,其将输入与输出参数进行了分离(用结构体实现).

函数如下:

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

参数含义:

  • fds:结构体数组,(可自定义数组的大小,以连接客户端的数目,所以以上可说其支持的文件描述符无上限,因为个数大小由用户可自定义大小),每一个结构体内容如下,并且可以看到其将输入输出型参数分离:
struct pollfd
{
    int fd;       //关心的文件描述符
    short events; //其作输入型参数指定fd关心的事件
    short revents;//其作输出型参数指定fd发生的哪一个事件就绪
};
  • nfds:结构体数组的大小;
  • timeout:与select含义相同,不过此处timeout不为结构体,只为一个整数值可直接设定,代表ms,含义如下:
    NULL:poll则一直阻塞等待事件就绪;
    0:则仅检测fd状态后不等待立即返回;
    特定值:在指定值时间内没有相应事件就绪则超时返回。

poll服务器代码实现如下:
poll_server.c

#include <stdio.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define SIZE 64

static void usage(const char* porc)
{
    printf("%s [local_ip] [local_porc]\n",porc);
}


int startup(char* ip,int port)
{
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("sock");
        close(sock);
        exit(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");
        close(sock);
        exit(3);
    }

    if(listen(sock,10)<0)
    {
        perror("sock");
        close(sock);
        exit(4);
    }

    return sock;
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }

    int listen_sock=startup(argv[1],atoi(argv[2]));    //创建监听套接字 

    struct pollfd fds[SIZE];     //poll数组以此来保存每一个客户端信息 
    int i=0;
    for( ;i<SIZE;++i)    //初始化 
    {
        fds[i].fd=-1;
        fds[i].events=0;
        fds[i].revents=0;
    }

    fds[0].fd=listen_sock;
    fds[0].events=POLLIN;        
    fds[0].revents=0;

    int timeout=500;

    while(1)
    {
        switch(poll(fds,SIZE,timeout))      
        {
            case -1:
                perror("poll");
                break;
            case 0:
                printf("timeout...\n");
                break;
            default:      //成功 
                {
                    struct sockaddr_in client;
                    socklen_t len=sizeof(client);
                    char buf[1024];

                    int i=0;
                    for( ;i<SIZE;++i)
                    {
                        if(i==0 && fds[i].revents==POLLIN)             //为0则为监听套接字建立连接 
                        {
                            int new_sock=accept(listen_sock,(struct sockaddr*)& client,&len);
                            if(new_sock<0)
                            {
                                perror("accept");
                                close(new_sock);
                                continue;
                            }
                            printf("get a new client:[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

                            int j=1;
                            for( ;j<SIZE;++j)
                            {
                                if(fds[j].fd==-1)
                                    break;
                            }

                            if(j==SIZE)
                            {
                                close(new_sock);
                                continue;
                            }
                            fds[j].fd=new_sock;
                            fds[j].events=POLLIN;
                        }
                        else if(i!=0 && fds[i].revents==POLLIN)   //normal fd ready,不为0则直接读取 
                        {
                            ssize_t s=read(fds[i].fd,buf,sizeof(buf)-1);
                            if(s>0)
                            {
                                buf[s]=0;
                                printf("client# %s\n",buf);

                                fds[i].events=POLLOUT;    //读取成功则将此关心事件改为写 
                            }
                            else if(s==0)
                            {
                                printf("client quit!!!\n");
                                close(fds[i].fd);
                                fds[i].fd=-1;
                                continue;
                            }
                            else
                            {
                                perror("read");
                                continue;
                            }
                        }
                        else if(i!=0 && fds[i].revents==POLLOUT)
                        {
                            ssize_t s=write(fds[i].fd,buf,strlen(buf));
                            if(s<0)
                            {
                                perror("write");
                                continue;
                            }
                            fds[i].events=POLLIN;    //写成功则将此关心事件改为读 
                        }
                    }
                }
                break;
        }
    }
    return 0;
}

从上可知,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket,事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值