poll 實現 I/O 複用
阿新 • • 發佈:2020-12-08
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(intargc, 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