(五十四)高并发服务器——多路IO转接机制poll模型

本文部分转载于:
http://blog.csdn.net/wqx521/article/details/53783222
http://blog.csdn.net/orz415678659/article/details/8958415
  
  
  poll提供的功能与select类似,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制,而且在处理流设备时,它能够提供额外的信息。
  poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
  另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
  
  函数原型如下:

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {
    int fd; /* 文件描述符 */
    short events; /* 监控的事件 */
    short revents; /* 监控事件中满足条件返回的事件 */
};

#define POLLIN      0x0001  //不阻塞地可读高优先级外的数据  
#define POLLRDNORM  0x0040  //不阻塞地可读普通数据  
#define POLLRDBAND  0x0080  //不阻塞地可读非0优先级波段数据  
#define POLLPRI     0x0002  //不阻塞地可读高优先级数据  

#define POLLOUT     0x0004  //不阻塞地可写普通数据  
#define POLLWRNORM  0x0100  //不阻塞地可写普通数据  
#define POLLWRBAND  0x0200  //不阻塞地可写非0优先级波段数据  

#define POLLERR     0x0008  //已出错  
#define POLLHUP     0x0010  //已挂断  
#define POLLNVAL    0x0020  //描述符不引用-打开文件  

nfds 监控数组中有多少文件描述符需要被监控

timeout  
-1  永远等待  
0   不等待  
>0   等待时间  

  
  
  
C/S模型举例:
注:其中wrap.h和wrap.c请查看第五十篇博客

/* server.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 8000
#define OPEN_MAX 1024

int main(int argc, char *argv[])
{
    int i, j, maxi, listenfd, connfd, sockfd;
    int nready;
    ssize_t n;
    char buf[MAXLINE], str[INET_ADDRSTRLEN];
    socklen_t clilen;
    struct pollfd client[OPEN_MAX];
    struct sockaddr_in cliaddr, servaddr;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    Listen(listenfd, 20);

    client[0].fd = listenfd;
    client[0].events = POLLRDNORM; /* listenfd监听普通读事件*/

    for (i = 1; i < OPEN_MAX; i++)
        client[i].fd = -1; /* 用-1初始化client[]里剩下元素*/
    maxi = 0; /* client[]数组有效元素中最大元素下标*/
    while(1)
    {
        nready = poll(client, maxi+1, -1); /* 阻塞*/
        if (client[0].revents & POLLRDNORM)
        { /* 有客户端链接请求*/
            clilen = sizeof(cliaddr);
            connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);

            printf("received from %s at PORT %d\n",
                    inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                    ntohs(cliaddr.sin_port));

            for (i = 1; i < OPEN_MAX; i++)
                if (client[i].fd < 0)
                {
                    /* 找到client[]中空闲的位置,存放accept返回的connfd */
                    client[i].fd = connfd;
                    break;
                }
            if (i == OPEN_MAX)
                perr_exit("too many clients");

            /* 设置刚刚返回的connfd,监控读事件*/ 
            client[i].events = POLLRDNORM; 

            if (i > maxi)
                maxi = i; /* 更新client[]中最大元素下标*/
            if (--nready <= 0)
                continue; /* 没有更多就绪事件时,继续回到poll阻塞*/
        }


        for (i = 1; i <= maxi; i++)
        { /* 检测client[] */
            if ( (sockfd = client[i].fd) < 0)
                continue;//不是响应的客户端就跳过

            if (client[i].revents & (POLLRDNORM | POLLERR))
            {//是读事件
                if ( (n = Read(sockfd, buf, MAXLINE)) < 0)
                {
                    if (errno == ECONNRESET)
                    { /* 当收到RST标志时*/
                        /* connection reset by client */
                        printf("client[%d] aborted connection\n", i);
                        Close(sockfd);
                        client[i].fd = -1;
                    } else
                        perr_exit("read error");
                } else if (n == 0)
                {
                    /* connection closed by client */
                    printf("client[%d] closed connection\n", i);
                    Close(sockfd);
                    client[i].fd = -1;
                } else
                {
                    for (j = 0; j < n; j++)
                        buf[j] = toupper(buf[j]);
                    Writen(sockfd, buf, n);
                }
                if (--nready <= 0)
                    break; /* no more readable descriptors */
            }
        }
    }
    return 0;
}
/* client.c */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    int sockfd, n;
    sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
    servaddr.sin_port = htons(SERV_PORT);
    Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    while (fgets(buf, MAXLINE, stdin) != NULL) {
        Write(sockfd, buf, strlen(buf));
        n = Read(sockfd, buf, MAXLINE);
        if (n == 0)
            printf("the other side has been closed.\n");
        else
            Write(STDOUT_FILENO, buf, n);
    }
    Close(sockfd);
    return 0;
}
发布了73 篇原创文章 · 获赞 17 · 访问量 30万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章