多路转接poll

文章目录

poll

  • 和select一样,poll也是一种就绪事件通知方案。poll也只有一个作用就是等。
  • poll解决两个select的问题:poll等待的文件描述符没有上限
  • poll将输入和输出利用变量做了分离。所以每次循环都不需要再次进行重新添加。

poll函数介绍

 #include <poll.h>
 int poll(struct pollfd *fds, nfds_t nfds, int timeout);     

第一个参数fds,是一个结构体数组,因为数组传参会退化成指针。

struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

  • fd代表你想要监控的文件描述符。
  • events是用户告诉内核,代表你想要操作系统帮你监控哪些事件。
  • revents是内核告诉用户,代表操作系统告诉用户,该文件描述符的哪些事件就绪。
  • pollfd将输入和输出参数做了分离,每次添加新的fd,旧的数组不需要改变。减少了轮询的过程。

第二个参数:
nfds代表fd的数量。

第三个参数:

  • 代表你想要poll阻塞等待一次的时间,单位是毫秒。超过时间后,会超时返回,跟select一样。
  • timeout设置为0,表示poll非阻塞等待。
  • timeout设置为-1,表示阻塞等待。

返回值:

  • 返回值和select的完全一样。

事件的设置

  • events和revents中,有很多事件。
  • 这里介绍两个,POLLIN和POLLOUT。分别代表可读就绪和可写就绪。
  • 这里使用的方法和open函数的类似,因为events是short类型,所以short的每一个比特位代表一种事件。我们使用按位或来添加事件,这样可以同时监测一个fd中的不同事件。
  • 我们使用按位与来检测 revents。

使用poll

监测标准输入:

  1 #include <iostream>
  2 #include <poll.h>
  3 #include <cstdlib>
  4 #include <unistd.h>
  5 
  6 using namespace std;
  7 
  8 int main(){
  9   //int poll(struct pollfd *fds, nfds_t nfds, int timeout);                                          
 10   struct pollfd fds[1];
 11   fds[0].fd = 0;
 12   fds[0].events = POLLIN;
 13   fds[0].revents = 0; //revents 可以不设置,由内核设置。
 14 
 15     char buf[1024] = {0};
 16   for(;;){
 17     cout << "poll begin..." << endl;
 18     int ret = poll(fds, 1, 1000);  //timeout的单位是毫秒!
 19     if(ret < 0){
 20       cerr << "poll error" << endl;
 21       exit(1);
 22     }
 23     else if(ret == 0){
 24       cout << "timeout..." << endl;
 25       continue;
 26     }
 27     else{
 28         ssize_t ss = read(0, buf, sizeof(buf) - 1);
 29         buf[ss] = 0;
 30         cout << "echo # "<< buf << endl;
 31     }
 32   }

  • 如果将监控POLLIN改成监控POLLOUT,那么就会一直打印poll begin,因为输出缓冲区一直空着,一直有空间。读取看的是有没有数据,写入看的是有没有空间。

poll的伪代码:

struct polllfd fds[MAX];  //MAX的值由你来设定;
//这里将fds[i].fd都初始化成一个负数,用来标识未被使用!
fds[0].fd = lsock;
fds[0].events = POLLIN;

for(;;){
	poll(fds, MAX, 1000);
	for(int i = 0; i < MAX; ++i){
		if(fds[i].fd == FLAG) //FLAG是你设置的负数,表示该位置未被使用
		continue;
		else if(fds[i].events & POLLIN) //这里还可以检测多种事件,
		{
			if(fds[i].fd == lsock) //连接
			{
				sock add to fds; //是连接就加入到fds中,然后设置想要检测的事件。
				sock add events
			}
			else
			{/** 处理数据 **/}
		}
		else if(fds[i].events & POLLOUT) //处理写事件,和上面一样的逻辑!
		{/* 略 */}
	}
}

poll优缺点

  • 比起select,poll不用做更多的轮询检测。
  • poll检测的文件描述符没有上限。

缺点:

  • 仍然避免不了用户到内核,内核到用户的数据拷贝。
  • 虽然用户没有进行过多的轮询,但是在内核中,仍然要使用轮询检测,这样文件描述符多了之后,效率仍会下降。

多路转接

不要神话多路转接:

  • 多路转接不是任何情况都适用的,要看场景。
  • 多路转接适用于长连接,长距离通信。这类场景等待的时间比重较大。
  • 即多路转接是为了减少等的比重,如果等待的时间本来就少,那么多路转接就没有太大意义。甚至还不如其他的IO模型。
  • 因为此时多路转接的轮询或者拷贝就会相当耗费资源,远远超过等的资源。

多路转接还要考虑上层协议:

  • 多路转接只保证数据高于水位线就给你,但是不保证数据的完整。
  • 比如你想要1024个字节的http报文,但是多路转接接收到100个字节就通知你。那么你需要先将100个字节数据存起来,然后再将该文件描述符重新设置进数组,继续等待。
  • 等待完毕,将1024个字节进行解析,然后生成一个response,以写事件的方式塞入多路转接,然后发出去。
上一篇:只做预览页面时,禁止鼠标点击事件


下一篇:11 JSP详解 Filter过滤器监听器 Junit