I/Oの多重化2
poll関数
#include <poll.h> int poll(struct pollfd *darray, unsigned long nfds, int timeout); 戻り値:準備のできているディスクリプタ数、タイムアウトなら0、エラーなら-1
struct pollfd { int fd; //検査するディスクリプタ short event;//fd上で興味のあるイベント short revent;//fd上で発生したイベント };
//pollによるechoサーバー #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <limits.h> #include <poll.h> #define MAXLINE 64 #define OPEN_MAX 128 //ディスクリプタからnバイト読み出す ssize_t readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft) ) < 0) { if (errno == EINTR) nread = 0;// 再度 read()を呼び出す else return -1; } else if (nread == 0) { break;// EOF } nleft -= nread; ptr += nread; } return (n - nleft); } //writen //ディスクリプタへのnバイトの書き込み ssize_t writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft) ) <= 0) { if (errno == EINTR) nwritten = 0;// 再度write()を呼び出す else return -1;// エラーの発生 } nleft -= nwritten; ptr += nwritten; } return n; } //readline //ディスクリプタから1バイトずつ1行分読み出す ssize_t readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { again: if ( (rc = read(fd, &c, 1) ) == 1) { *ptr++ = c; if (c == '\n') break; } else if (rc == 0) { if (n == 1) return 0;// EOF データなし else break; } else { if (errno == EINTR) goto again; return -1;// エラー発生 readがerrnoを設定している } } *ptr = 0;//終端をヌルにする return n; } void str_echo(int sockfd) { ssize_t n; char line[MAXLINE]; for (;;) { if ( (n = readline(sockfd, line, MAXLINE) ) == 0) { return; } writen(sockfd, line, n); } } int main() { int i, maxi, listenfd, connfd, sockfd; int nready; ssize_t n; char line[MAXLINE]; 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(8004); bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); listen(listenfd, 64); client[0].fd = listenfd; client[0].events = POLLIN; for (i = 1; i < OPEN_MAX; i++) client[i].fd = -1;//-1は利用可能なエントリを示す maxi = 0;//client[]配列の最大添え字 for (;;) { nready = poll(client, maxi + 1, 1); if (client[0].revents & POLLIN) {//新規クライアントコネクション clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen); for (i = 1; i < OPEN_MAX; i++) { if (client[i].fd < 0) { client[i].fd = connfd;//ディスクリプタの保存 break; } } if (i == OPEN_MAX) { perror("too many clients"); exit(1); } client[i].events = POLLIN; if (i > maxi) maxi = i; if (-nready <= 0) continue;//読み出し可能なディスクリプタがない } for (i = 1; i <= maxi; i++) {//クライアントからのデータ検査 if ( (sockfd = client[i].fd) < 0 ) continue; if (client[i].revents & (POLLIN | POLLERR)) { if ( (n = readline(sockfd, line, MAXLINE)) < 0 ) { if (errno == ECONNRESET) { //クライアントがコネクションをリセットした close(sockfd); client[i].fd = -1; } else { perror("readline error"); exit(1); } } else if (n == 0) { close(sockfd); client[i].fd = -1; } else { writen(sockfd, line, n); } if (-nready <= 0) break;//読み出し可能なディスクリプタがない } } } return 0; }
参考文献