I/O多路转接---poll服务器实现

poll其实和select服务器实现的功能是一样的,但是poll中有具体的函数来帮助我们实现I/O多路转接。

我们来认识一下这些函数。

poll函数
#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout)
pollfd 结构
struct pollfd
{
  int fd;                     /* File descriptor to poll.  */
  short int events;           /* Types of events poller cares about.  */
  short int revents;          /* Types of events that actually occurred.      */
};

pollfd结构中将输入事件和输出事件分开了,用结构体来管理,解决了select每次都要重置fd_set,将关心的事件和就绪的事件进行区分。

参数:
* fds是一个poll函数监听的结构列表,每一个元素中,包含了三部分内容:文件描述符,监听的事件集合,返回的事件集合。
* nfds表示fds数组的长度。
* timeout表示poll函数的超时时间,单位是毫秒。
events、revents的取值
事件描述是否可作输入是否可作输出
POLLIN数据(包括普通数据和优先数据)可读
POLLRDNORM普通数据可读
POLLRDBAND优先带数据可读(Linux不支持)
POLLPRI高优先级数据可读
POLLOUT数据可写
POLLWRNORM普通数据可写
POLLWRBAND优先数据可写
POLLRDHUPTCP连接被对方关闭,或对方关闭了写操作,由GNU引入
POLLERR错误
POLLHUP挂起
POLLNVAL文件描述符没有打开

poll的返回值和select的一样。

* 小于0,表示出错。
* 等于0,表示poll函数等待超时。
* 大于0,表示poll由于监听的文件描述符就绪而返回。

socket就绪条件

1.读就绪
* socket内核中,接收缓冲区中的字节数大于等于低水位SO_RCVLOWAT,此时可以无阻塞的读该文件描述符,并且返回值大于0.
* socket TCP通信中,对端关闭连接,此时对该socket读,则返回0.
* 监听的socket上有新的连接请求
* socket上有未处理的错误。
2.写就绪
* socket内核中,发送缓冲区中的可用字节数,大于等于低水位SO_SNDLOWAT,此时可以无阻塞的写,并且返回值大于0.
* socket的写操作被关闭,对于一个写操作被关闭的socket进行写操作,会出发SIGPIPE信号.
* socket使用非阻塞connect连接成功或失败之后。
* socket上有未读取的错误。
3.异常就绪
* socket上收到带外数据。

poll的优点

* pollfd结构中包含了要监视额events和revents,不再使用select值传递的方式,接口较简单。
* poll中文件描述符数量没有限制,是因为可以在堆上malloc空间。

事实上,文件描述符是有大小限制的,不可能是无限的。因为PCB指向文件描述符表的限制,所以poll的性能可能最后趋于平静。

poll的缺点

当poll中的文件描述符越来越多的时候,poll返回后,需要轮询pollfd来获取就绪的描述符。
和select一样,每次调用都需要把大量的pollfd结构从用户态拷贝到内核中。
随着文件描述符的增多,可能会出现只有少量的事件就绪,会导致效率降低。poll的性能是从低到高,最后趋于平静的。

使用poll监视标准输入

#include <stdio.h>
#include <poll.h>
#include <unistd.h>

int main()
{
    struct pollfd fd;
    fd.fd = 0;
    fd.events = POLLIN;//数据可读

    for(;;){
        int ret = poll(&fd, 1, 2000);         
        if(ret<0){
            perror("poll");
            continue;
        }
        if(ret==0){
            printf("poll timeout\n");
            continue;
        }
        if(fd.revents == POLLIN){
            char buf[1024] = {0};
            read(0, buf, sizeof(buf-1));
            printf("输入:%s",buf);
        }
    }
    return 0;
}

这里写图片描述

poll多路转接服务器

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

typedef struct pollfd pollfd;

int start_up(int port)
{
     int sock = socket(AF_INET, SOCK_STREAM, 0);
     if(sock<0)
     {
         perror("socket");
         exit(2);
     }

     //设置没有TIME_WAIT
     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 = htonl(INADDR_ANY);

     int ret = bind(sock, (struct sockaddr*)&local, sizeof(local));
     if(ret < 0)
     {
         perror("bind");
         exit(3);
     }

     ret = listen(sock, 5);
     {
         if(ret < 0)
         {
             perror("listen");
             exit(4);
         }
     }
     return sock;
}

void Init(pollfd* fd_list, int size)
{
     int i =0;
     for(i=0; i<size; ++i)
     {
         fd_list[i].fd = -1;                   
         fd_list[i].events = 0;
         fd_list[i].revents = 0;
     }
}

void Add(int fd, pollfd* fd_list, int size)
{
     int i=0;
     for(; i<size; ++i)
     {
         if(fd_list[i].fd == -1){
             fd_list[i].fd = fd;
             fd_list[i].events = POLLIN;//数据可读
             break;
         }
     }
}


int main(int argc, char* argv[])
{
     if(argc != 2)
     {
         printf("Usage: ./server [port]\n");
         return 1;
     }

     int listen_fd = start_up(atoi(argv[1]));
     pollfd fd_list[1024];
     Init(fd_list, sizeof(fd_list)/sizeof(pollfd));
     Add(listen_fd, fd_list, sizeof(fd_list)/sizeof(pollfd));
     for(;;)
     {
         int ret = poll(fd_list, sizeof(fd_list)/sizeof(pollfd),9000);
         if(ret < 0)
         {
             perror("poll");
             continue;
         }
         if(ret == 0)
         {
             printf("timeout...");
             continue;
         }

         int i=0;
         for(;i<sizeof(fd_list)/sizeof(pollfd);++i){
         if(fd_list[i].fd==-1)
             continue;
         if(!fd_list[i].revents & POLLIN)
             continue;
         if(fd_list[i].fd==listen_fd)
         {
             struct sockaddr_in client;                      
             socklen_t len = sizeof(client);
             int connect_fd = accept(listen_fd, (struct sockaddr*)&client,&len);
             if(connect_fd < 0){
                 perror("accept");
                 continue;
             }
             Add(connect_fd, fd_list, sizeof(fd_list)/sizeof(pollfd));
         }
         else
         {
             char buf[1024]={0};
             ssize_t read_size = read(fd_list[i].fd, buf, sizeof(buf)-1);
             if(read_size < 0)
             {
                 perror("read");
                 continue;
             }
             if(read_size == 0)
             {
                 printf("client is closed\n");
                 close(fd_list[i].fd);
                 fd_list[i].fd = -1;
             }
             printf("client:>%s\n",buf);
             write(fd_list[i].fd, buf, strlen(buf));

         }
         }
     }

}      

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值