网络编程(50)—— 使用poll搭建回声服务端

一、 引言

        本文主要介绍在linux中使用poll搭建回射服务端。我们在前面的文章中研究了使用select和epoll搭建服务端的方法。poll的用法和select类似,只不过用来描述操作符集合的是pollfd而非select的fd_set。Poll在Linux 2.5.44版本后被epoll取代,本文只为研究其用法。

二、函数原型

poll的函数原型如下:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
        fds是结构体pollfd数组的首地址,pollfd的定义如下:
struct pollfd{
  int fd; //要监视的文件描述符
  short events; //请求注册的事件
  short revents; //返回的事件
  };、
        使用pollfd时,我们在events中注册需要监视的事件,每个事件都用数据位的宏表示:
常量 说明

常量

说明

POLLIN

普通或优先级带数据可读

POLLRDNORM

普通数据可读

POLLRDBAND

优先级带数据可读

POLLPRI

高优先级数据可读

POLLOUT

普通数据可写

POLLWRNORM

普通数据可写

POLLWRBAND

优先级带数据可写

POLLERR

发生错误

POLLHUP

发生挂起

POLLNVAL

描述字不是一个打开的文件


        当poll函数返回时,会将发生的事件写到revents里面,如果没发生事件,revents将会被自动清零。与select的fd_set不同,poll每当调用这个函数之后,系统不会清空fds这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只监视一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;

        nfds是fds中元素的个数,也就是我们注册的需要监视的文件描述符的个数。

        timeout是超时,设置成INFTIM,一直等待到事件的发生;设置成0不进行阻塞;设置成大于0的整数,表示等待阻塞的时间,单位是毫秒。

        poll函数返回-1时,表示运行出错;返回0时表示等待超时;返回大于0的数字表示发生了事件的操作符的个数。

三、使用范例

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

#define BUF_SIZE 30
#define POLL_SIZE 20


void error_handler(const char* message);
int main(int argc,char* argv[])
{
    int serv_sock,clnt_sock;
    struct sockaddr_in serv_addr,clnt_addr;
    int clnt_addr_sz,str_len;
    char buf[BUF_SIZE];

    struct pollfd fds[POLL_SIZE];
    int plnum,fpnum,i,j;

    if(argc!=2)
    {
        printf("Usage %s <port>\n",argv[0]);
        exit(1);
    }
    
    serv_sock=socket(AF_INET,SOCK_STREAM,0);
    
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_addr.sin_port=htons(atoi(argv[1]));

    if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
        error_handler("bind error");

    if(listen(serv_sock,5)==-1)
        error_handler("listen error");

    for(i=0;i<POLL_SIZE;i++)
        fds[i].fd=-1;
    fds[0].fd=serv_sock;
    fds[0].events=POLLIN;
    fpnum=0; 
    while(1)
    {
        plnum=poll(fds,fpnum+1,INFTIM);
        printf("poll returned");
        if((fds[0].revents&POLLIN)==POLLIN)
        {
            clnt_addr_sz=sizeof(clnt_addr);
            clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_sz);
            for(i=0;i<POLL_SIZE;i++)
            {
                if(fds[i].fd<0)
                {
                    fds[i].fd=clnt_sock;
                    fds[i].events=POLLIN;
                }
            }
            if(fpnum<i)
                fpnum=i;
        }

        for(i=1;i<fpnum;i++)
        {
            if(fds[i].fd<0) continue;
            str_len=read(fds[i].fd,buf,BUF_SIZE);
            if(str_len<=0)
            {
                close(fds[i].fd);
                fds[i].fd==-1;
            }
            else
            {                     
                write(fds[i].fd,buf,str_len);
            }
        }
    }

    return 0;
}

void error_handler(const char* message)
{
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}

第20~21行,声明使用poll函数相关API。fds是我们用来存放需要监视的pollfd结构体变量的数组。

第42~43行,将eps所有元素的fd都置为-1,以便和正常的文件操作符进行区分。

第44~45行,向fds中注册serv_sock。

第49行,利用poll监视注册的socket。

第51行,判断是不是serv_sock触发POLLIN状态,如果是调用accept接收客户端连接。

第55~62行,将接收的clnt_sock注册到pds中。

第67~79行,接收客户端的数据,并进行回射。


Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL50





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值