poll
■ 函数说明
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
The set of file descriptors to be monitored is specified in the fds argument, which is an array of structures of the following form:
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
The caller should specify the number of items in the fds array in nfds.
The timeout argument specifies the number of milliseconds that poll() should block waiting for a file descriptor to become ready. The call will
block until either:
* a file descriptor becomes ready;
* the call is interrupted by a signal handler; or
* the timeout expires.
RETURN VALUE
On success, a positive number is returned; this is the number of structures which have nonzero revents fields (in other words, those descriptors with
events or errors reported). A value of 0 indicates that the call timed out and no file descriptors were ready. On error, -1 is returned, and errno
is set appropriately.
▶ struct pollfd
The field fd contains a file descriptor for an open file. If this field is negative, then the corresponding events field is ignored and the revents
field returns zero. (This provides an easy way of ignoring a file descriptor for a single poll() call: simply negate the fd field. Note, however,
that this technique can't be used to ignore file descriptor 0.)
The field events is an input parameter, a bit mask specifying the events the application is interested in for the file descriptor fd. This field may
be specified as zero, in which case the only events that can be returned in revents are POLLHUP, POLLERR, and POLLNVAL (see below).
The field revents is an output parameter, filled by the kernel with the events that actually occurred. The bits returned in revents can include any
of those specified in events, or one of the values POLLERR, POLLHUP, or POLLNVAL. (These three bits are meaningless in the events field, and will be
set in the revents field whenever the corresponding condition is true.)
▶ events and revents
POLLIN There is data to read.
POLLPRI
There is urgent data to read (e.g., out-of-band data on TCP socket; pseudoterminal master in packet mode has seen state change in
slave).
POLLOUT
Writing is now possible, though a write larger that the available space in a socket or pipe will still block (unless O_NONBLOCK is
set).
POLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection. The _GNU_SOURCE feature test macro must be defined
(before including any header files) in order to obtain this definition.
POLLERR
Error condition (only returned in revents; ignored in events).
POLLHUP
Hang up (only returned in revents; ignored in events). Note that when reading from a channel such as a pipe or a stream socket, this
event merely indicates that the peer closed its end of the channel. Subsequent reads from the channel will return 0 (end of file) only
after all outstanding data in the channel has been consumed.
POLLNVAL
Invalid request: fd not open (only returned in revents; ignored in events).
When compiling with _XOPEN_SOURCE defined, one also has the following, which convey no further information beyond the bits listed above:
POLLRDNORM
Equivalent to POLLIN.
POLLRDBAND
Priority band data can be read (generally unused on Linux).
POLLWRNORM
Equivalent to POLLOUT.
POLLWRBAND
Priority data may be written.
■ poll使用步骤
#include<poll.h>
#define IN_FILES 2
struct pollfd fds[IN_FILES];
fds[0].fd = fd1;
fds[0].events = POLLIN;
fds[1].fd = fd2;
fds[1].events = POLLIN;
int result;
result = poll(fds, IN_FILES, 3000);
if (result == 0) {
printf("Poll timeout\n");
} else if (result > 0) {
printf("Poll successfully\n");
} else {
printf("Poll error\n");
}
if (fds[0].revents == POLLIN)
{
fds[0].revents &= ~POLLIN;
//do callback for fd1
}
if (fds[1].revents == POLLIN)
{
fds[1].revents &= ~POLLIN;
//do callback for fd2
}
注意:
使用者关注fd1的POLLIN events fds[0].fd = fd1; fds[0].events = POLLIN;
poll之后,用户关注是fds[0].revents
使用的是fds[0]的不同参数。
■ 例子程序
server.c
//server.c #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<poll.h> #include <unistd.h> #define DEFAULT_PORT 8000 #define MAXLINE 4096 #define IN_FILES 1//我们这里poll机制只监听一个文件描述符 void socket_accept(int socket_fd) { int connect_fd; char buff[MAXLINE]; //阻塞直到有客户端连接,不然浪费CPU资源,但是显然这里通过poll机制是有可读的信息才会进入本函数,因此应该是不会阻塞的。 if ((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1) { printf("accept socket error: %s(errno: %d)",strerror(errno),errno); } int n = recv(connect_fd, buff, MAXLINE, 0); buff[n] = '\0'; printf("recv msg from client: %s\n", buff); if (send(connect_fd, "Hello,you are connected!\n", 26, 0) == -1) { perror("send error"); } close(connect_fd); } int main(int argc, char** argv) { int socket_fd, connect_fd; struct sockaddr_in servaddr; struct pollfd fds[IN_FILES]; //初始化Socket if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { printf("create socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //初始化 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。 servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT //将本地地址绑定到所创建的套接字上 if (bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) { printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //开始监听是否有客户端连接 if (listen(socket_fd, 10) == -1) { printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } fds[0].fd = socket_fd; fds[0].events = POLLIN; int result; while(1){ printf("begin to poll()\n"); printf("fds[0].fd = %d fds[0].events = %d fds[0].revents = %d POLLOUT = %d\n",fds[0].fd , fds[0].events, fds[0].revents, POLLOUT); result = poll(fds, IN_FILES, -1); //It may block with timeout if (result == 0) { printf("Poll timeout\n"); } else if (result > 0) { printf("Poll successfully\n"); } else { printf("Poll error\n"); } if (fds[0].revents == POLLIN){ fds[0].revents &= ~POLLIN; printf("===receive a connetction====\n"); socket_accept(socket_fd); } printf("do something else\n"); sleep(1); printf("do something else end\n"); } close(socket_fd); }
client.c
//client.c // run$ ./client.o 127.0.0.1 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #define MAXLINE 4096 int main(int argc, char** argv) { int sockfd, n,rec_len; char recvline[4096], sendline[4096]; char buf[MAXLINE]; struct sockaddr_in servaddr; if (argc != 2) { printf("usage: ./client <ipaddress>\n"); exit(0); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("create socket error: %s(errno: %d)\n", strerror(errno),errno); exit(0); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8000); if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) { printf("inet_pton error for %s\n",argv[1]); exit(0); } if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { printf("connect error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("send msg to server: \n"); fgets(sendline, 4096, stdin); if (send(sockfd, sendline, strlen(sendline), 0) < 0) { printf("send msg error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } if ((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) { perror("recv error"); exit(1); } buf[rec_len] = '\0'; printf("Received : %s ",buf); close(sockfd); exit(0); }
Makefile
#https://www.cnblogs.com/muhe221/articles/4487763.html all : gcc server.c -o server gcc client.c -o client clean: rm -f server client .PHONY:all clean
运行:
./server
./client 127.0.0.1