1. 程式人生 > 實用技巧 >poll 實現 I/O 複用

poll 實現 I/O 複用

poll

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

使用 poll 可以實現同時監聽多個檔案描述符

fds

fds 是一個 struct pollfd 的結構體陣列。其結構如下

struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /*
returned events */ };

把 fd 設定為想要監聽的 檔案描述符。

把 events 設定為想要監聽的事件。通過設定位的方式來設定。

比如:fds.events = POLLIN | POLLOUT

POLLIN:有資料可讀

POLLOUT:可以寫資料

如果 有資料可讀 或者 可以寫資料 那麼 revents 上對應的位就會置 1

● revents 和 events 類似,代表有哪些事件可以進行

nfds

代表 fds 中元素的個數。(元素:fd 不為 -1 的結構體)

一般設定成 maxi + 1,maxi 為 fds陣列 中最右面元素的索引。

timeout

負數  代表一直等待

0  立即返回

正數  指定等待的毫秒數

返回值

就緒描述符的個數,0 代表超時,出錯返回 -1

簡單例項

伺服器程式

監聽請求連線的描述符,並接受資料列印到螢幕

#include <stdio.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <errno.h>

#define MAXLINE 1024
#define MAXPOLL 100

int main(int
argc, char *argv[]) { char buf[MAXLINE]; int listenfd, clientfd; struct pollfd fds[MAXPOLL]; nfds_t nfds; int nready; int i, nread; struct sockaddr_in sock; socklen_t socklen; listenfd = socket(AF_INET, SOCK_STREAM, 0); if(listenfd < 0) { perror("socket error:"); return -1; } memset(&sock, 0, sizeof(sock)); sock.sin_family = AF_INET; sock.sin_addr.s_addr = htonl(INADDR_ANY); sock.sin_port = htons(18888); socklen = sizeof(sock); if(bind(listenfd, (struct sockaddr *)&sock, socklen) != 0) { perror("bind error:"); return -1; } if(listen(listenfd, 1024) != 0) { perror("listen error\n"); return -1; } nfds = 1; fds[0].fd = listenfd; fds[0].events = POLLIN; //POLLIN: there is data to read for(i = 1; i < MAXPOLL; i++) fds[i].fd = -1; while(1){ nready = poll(fds, nfds, -1); //specify negative value mean infinite time if(nready == -1) { perror("poll error\n"); return -1; } if(fds[0].revents & POLLIN) { //new client clientfd = accept(listenfd, NULL, NULL); for(i = 1; i < MAXPOLL; i++) if(fds[i].fd == -1) { fds[i].fd = clientfd; fds[i].events = POLLIN; break; } if(i == MAXPOLL) { fprintf(stderr, "too many client\n"); return -1; } nfds = nfds > (i + 1) ? nfds : (i + 1); nready--; } while(nready) { //data can read for(i = 1; i < nfds; i++){ if(fds[i].fd == -1) continue; if(fds[i].revents & POLLIN){ nread = read(fds[i].fd, buf, MAXLINE-1); if(nread < 0) { if(errno != ECONNRESET){ perror("read error:"); return -1; } close(fds[i].fd); fds[i].fd = -1; nready--; continue; } if(nread == 0) { close(fds[i].fd); fds[i].fd = -1; nready--; continue; } printf("%s\n", buf); nready--; } } } } }

客戶端程式

請求連結併發送資料

#include <stdio.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>

#define MAXLINE 1024

int main(int argc, char *argv[])
{
    int fd;
    int rv, n, nwrite;
    char buf[MAXLINE] = "hello world";
    struct addrinfo hint, *ai, *airoot;

    if(argc != 3){
        fprintf(stderr, "usage: ./a.out hostname servicenaeme\n");
        return 0;
    }
    memset(&hint, 0, sizeof(hint));
    hint.ai_family = AF_UNSPEC;
    hint.ai_socktype = SOCK_STREAM;
    rv = getaddrinfo(argv[1], argv[2], &hint, &airoot);
    if(rv != 0){
        fprintf(stderr, "getaddrinfo error\n");
        return 0;
    }
    for(ai = airoot; ai; ai = ai->ai_next){
        fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        if(fd < 0)
            continue;
        rv = connect(fd, ai->ai_addr, ai->ai_addrlen);
        if(rv == 0)
            break;
        close(fd);
    }
    printf("please input, q to quit:\n");
    while(scanf("%s", buf) != EOF) {
        nwrite = write(fd, buf, strlen(buf) + 1);
        if(buf[0] == 'q')
            break;
        printf("write %d bytes\n", nwrite);
    }
    close(fd);
    return 0;
}

POLLIN