poll函数的详细解释及使用


最近一直在看UNP,2020年8月的暑假初学的网络编程,最开始接触的是windows的select函数。尽管select函数(我的博客首页置顶的小QQ就拿select做的,所以不打算写关于select函数的使用说明)和poll函数在实际开发并不会用到,但还是想学一下,也不是很难使用,花两三个小时做一下实验还是值的的!

参数解释

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
第一个参数是一个结构体,结构体里的成员变量fd也可以理解为存放客户端的套接字,events是给这个对象设置什么样子的事件,revents是返回事件的结果。第三个参数是超时,-1是一直阻塞直到有事件产生。0是非阻塞,>0就是设置一个超时时间。

返回值

-1表示出错了,0表示超时了,>0表示产生时间的个数

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

思路讲解

当accept成功产生一个套接字的时候,我们可以遍历1——OPEN_MAX,那里有空地就插入到那里。在设置一下他的事件类型,然后就break。后面的循环用一个max_client来记录最大的套接字的下标,稍微提一下速度。

代码部分

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <poll.h>
#include <arpa/inet.h>
#include <sys/wait.h>
using namespace std;
constexpr int OPEN_MAX = 64;
#ifndef INFTIM
#define INFTIM -1
#endif
int main(int argc,char** argv){
	int server_socket = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
        if(server_socket == -1){
                cout<<"服务器套接字创建失败"<<endl;
        }
        sockaddr_in addr;
        addr.sin_family = AF_INET;
	int port = 9996;
	if(argc > 1){
		port = atoi(argv[1]);
	}
        addr.sin_port = htons(port);
        inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
        
	if (bind(server_socket,(sockaddr*)&addr,sizeof(addr)  ) < 0 ) {
                cout << "bind绑定失败" << endl;
                return -1;
        }
        if (listen(server_socket, SOMAXCONN) < 0) {
                cout << "监听失败" << endl;
                return -1;
        }
	struct pollfd client_fd[OPEN_MAX];
	client_fd[0].fd = server_socket;
	client_fd[0].events = POLLRDNORM;
	for(int i = 1;i< OPEN_MAX;++i){
		client_fd[i].fd = -1;
	}
	cout<<"服务器启动成功!"<<endl;
	char buff[1024] = { 0 };
	int max_client = 0;
	while(1){
			int nready = poll(client_fd,max_client+1,-1);
			if(client_fd[0].revents & POLLRDNORM){//说明有新的连接
		
				int client = accept(server_socket,NULL,NULL);
				if(client < 0){
         	              		if(errno == EINTR){
                  	       	      		 continue;
                       		      	 }
					continue;
               			}
				for(int i = 1;i < OPEN_MAX;++i){
					if(client_fd[i].fd != -1)continue;	
	       				client_fd[i].fd = client;
					client_fd[i].events = POLLIN;
					cout<<"有新的连接,i的下标为"<<i<<endl;
					max_client = max(i,max_client);
					break;
				}
			}
			for(int i = 1;i <= max_client;++i){
				if(client_fd[i].fd == -1){
					continue;
				}
				if(client_fd[i].revents & POLLIN){
				//	cout<<"接收数据"<<endl;
					int r = recv(client_fd[i].fd,buff,sizeof(buff),0);
					if(r == 0)
					{	
						cout<<"关闭连接"<<endl;
						close(client_fd[i].fd);
						client_fd[i].fd = -1;
						continue;
					}
					buff[r] = '\0';
					cout<<"服务器收到一条消息"<<buff<<endl;
					--nready;	
				}
			}
	}
	return 0;
}

makefile

我的makefile并没有自动执行,因为有时候如果先关闭服务器在关闭客户端,该端口会进入TIME_WAIT状态,端口绑定会失败,所以我一般都会手动./poll_server 执行,如果默认9996失败,我可以./poll_server 9997换一个端口绑定

poll_server:poll.cpp
        g++ $^ -o $@

clean:
        rm -f *.o

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`poll()` 函数用于在一组文件描述符上进行轮询操作,以确定是否有数据可读、可写或出错。它类似于 `select()` 函数,但具有更好的性能和可扩展性。 下面是 `poll()` 函数的原型: ```c #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout); ``` 其中: - `fds`:一个指向 `pollfd` 结构体数组的指针,每个结构体描述了一个需要轮询的文件描述符以及轮询的事件类型。 - `nfds`:需要轮询的文件描述符的数量。 - `timeout`:等待时间(以毫秒为单位),如果为负数,表示无限等待;如果为0,表示立即返回。如果大于0,则表示最长等待时间。 `pollfd` 结构体定义如下: ```c struct pollfd { int fd; // 文件描述符 short events; // 轮询的事件类型(POLLIN、POLLOUT 等) short revents; // 实际发生的事件类型 }; ``` 下面是一个简单的示例,展示如何使用 `poll()` 函数: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <poll.h> int main() { struct pollfd fds[2]; int ret; fds[0].fd = STDIN_FILENO; // 标准输入文件描述符 fds[0].events = POLLIN; // 轮询可读事件 fds[1].fd = STDOUT_FILENO; // 标准输出文件描述符 fds[1].events = POLLOUT; // 轮询可写事件 ret = poll(fds, 2, 5000); // 等待5秒钟 if (ret == -1) { perror("poll"); exit(EXIT_FAILURE); } if (ret == 0) { printf("Timeout\n"); } else { if (fds[0].revents & POLLIN) { printf("stdin is readable\n"); } if (fds[1].revents & POLLOUT) { printf("stdout is writable\n"); } } return 0; } ``` 在上面的示例中,我们创建了两个 `pollfd` 结构体,分别表示标准输入和标准输出文件描述符。然后使用 `poll()` 函数等待5秒钟,如果有数据可读或可写,则打印相关信息。注意,在处理 `revents` 字段时,需要使用位运算符来检查发生的事件类型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值