在工作中遇到了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

演示:

pois函数返回address poll返回值_文件描述符

用client往udp_tmp0发送字符串:

pois函数返回address poll返回值_文件描述符_02

用client往udp_tmp1发送字符串: 

pois函数返回address poll返回值_文件描述符_03

 

好了,就到这里,由于我也才看了一点点poll,也没测试它的很多用法,所以理解很浅显,欢迎大家交流。