(五十四)高併發服務器——多路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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章