在工作中遇到了poll()函数,决定来了解一下,参考《UNIX环境高级编程》。
我的理解是:poll()函数有一个监听池,我们把要监听的文件描述符以及我们对该描述符感兴趣的条件(读,写等等)放进池子里,然后就等poll()帮我们监听,等poll()正常返回时,就是有描述符发生了变化,我们通过遍历找到这个变化的文件描述符,再去进行相应的操作(读,写等等)即可。
函数原型:
#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
返回值:
准备就绪的描述符数目;若超时,返回0;若出错,返回-1。
参数说明:
pollfd结构如下,
struct pollfd{
int fd; /* file descriptor to check, or < 0 to ignore */
short events; /* events of interest on fd*/
short revents; /* events that occurred on fd*/
};
fd是监听的文件描述符,
events设置的值,是我们关心的每个文件描述符会发生的哪些事件
revents的值用作返回时,说明每个文件描述符发生了哪些事件,也就是实际发生的事件
events和revents的常用值有POLLIN,POLLRDNORM,POLLRDBAND等等,具体含义可以百度
nfds用来指定fdarray数组中的元素数目。经过测试,如果监听了两个fd,但是nfds==1的情况下,只有fdarray[0].fd能被监听到
timeout指定的是我们愿意等待多长时间。timeout==-1,永远等待;timeout==0,不等待;timeout>0,等待timeout毫秒。
实例:
说明:由于前几天刚看了socket本地进程间通信,所以就在此基础上进行poll()的使用。
我们知道,默认情况下,TCP下的accept和UDP下的recvfrom都是阻塞型,直到有client给其发送数据才会返回。我们可以在accept或者recvfrom之前调用poll来帮我们监听,等监听的文件描述符有变化了再去accept或者recvfrom。
本例子用socket本地进程间通信,基于UDP的。server端一共打开了两个本地socket文件描述符,将其加入poll监听池中,设置timeout为-1,即永久等待。然后client端分别向两个文件描述符发送数据,看看server端会有什么样的输出。下面给出代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
/*****************Server****************/
#define MAX_BUFFER_SIZE 1024
#define IN_FILES 2
int main(int argc ,char **argv)
{
struct pollfd fds[IN_FILES];
char buf[MAX_BUFFER_SIZE] = "", domain[32] = "";
int i;
int ret;
int server_sockfd[IN_FILES];
int server_len[IN_FILES];
struct sockaddr_un server_address[IN_FILES];
for(i=0; i<IN_FILES; i++)
{
//create server socket
sprintf(domain, "udp_tmp%d", i);
unlink(domain);
server_sockfd[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
if(server_sockfd[i]<0)
{
printf("socket error\n");
return -1;
}
//name the socket
server_address[i].sun_family = AF_UNIX;
strcpy(server_address[i].sun_path, domain);
server_len[i] = sizeof(server_address[i]);
bind(server_sockfd[i], (struct sockaddr *)&server_address[i], server_len[i]);
}
//set the fd and events in poll listen pool
for (i = 0; i < IN_FILES; i++)
{
fds[i].fd = server_sockfd[i];
fds[i].events = POLLIN;
}
while(1)
{
if (poll(fds, IN_FILES, -1) <= 0)
{
printf("Poll error\n");
return 1;
}
printf("********************\n");
for (i = 0; i< IN_FILES; i++)
{
printf("%d.revents = %d\n", i+1, fds[i].revents);
if ((fds[i].revents & POLLIN) != 0)
{
//accept a connection
ret = recvfrom(server_sockfd[i], buf, 1024, 0, (struct sockaddr *)&server_address[i], (socklen_t *)&server_len[i]);
//write and read by socket fd
buf[ret]= '\0';
printf("char from client is %s\n", buf);
}
}
}
//close
for (i = 0; i < IN_FILES; i++)
{
close(server_sockfd[i]);
sprintf(domain, "udp_tmp%d", i);
unlink(domain);
}
return 0;
}
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include <errno.h>
/*****************Client****************/
int main(int argc, char **argv)
{
int fd, ret, len;
struct sockaddr_un un;
char str[24] ;
memset(str, 0, sizeof(str));
strcpy(str, "Hello World!");
//create socket
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(fd<0)
{
printf("socket error\n");
return -1;
}
//name the socket
un.sun_family=AF_UNIX;
strcpy(un.sun_path, argv[1]);
len = sizeof(un);
ret = sendto(fd, str, strlen(str)+1, 0, (struct sockaddr*)&un, sizeof(struct sockaddr_un));
printf("ret = %d, errno = %d, %s\n", ret, errno, strerror(errno));
return 0;
}
gcc poll.c -o poll
gcc udp_client.c -o udp_client
演示:
用client往udp_tmp0发送字符串:
用client往udp_tmp1发送字符串:
好了,就到这里,由于我也才看了一点点poll,也没测试它的很多用法,所以理解很浅显,欢迎大家交流。