socket编程中poll怎么使用

这篇文章主要介绍“socket编程中poll怎么使用”,在日常操作中,相信很多人在socket编程中poll怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”socket编程中poll怎么使用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一. 关于poll

    对于IO复用模型,其优点无疑是免去了对一个个IO事件就绪的等待,转而代之的是同时对多个IO数据的检测,当检测等待的事件中至少有一个就绪的时候,就会返回告诉用户进程“已经有数据准备好了,快看看是哪个赶紧处理”,而对于IO复用的实现,除了可以用select函数,另外一个函数仍然支持这种复用IO模型,就是poll函数;


二. poll函数的用法

    虽然同样是对多个IO事件进行检测等待,但poll和select多少还是有些不同的:

socket编程中poll怎么使用

函数参数中,

先来说nfds,这个是指当前需要关心的文件描述符的个数;

timeout同样是设置超时时间,只是和select的timeout是一个结构体不一样,这里只是一个整型类型,且含义是毫秒

fds是一个结构体指针,如下:

socket编程中poll怎么使用

结构体中,

fd表示所要关心的文件描述符;

events表示该文件描述符所关心的事件,这是一个输入型参数,要告诉操作系统这个文件描述符对应的事件所关心的操作事件是什么,比如读或写;

revents是一个输出型参数,表示当poll返回时告诉用户什么操作事件是就绪的,比如如果POLLIN是就绪的,那么返回时revent的值就是POLLIN,告诉用户fd事件的POLLIN是就绪的;

events和revents的值可以为如下:

socket编程中poll怎么使用

选项其实不止这三个,只是这里的讨论中这三个选项是最常用的;

events设置为POLLIN表示fd所需要读取数据,而revents若返回POLLIN则表示data已经ready可以读取了;

同样,events设置为POLLOUT表示fd所关心数据的写入,而revents返回POLLOUT则表示写事件就绪可以进行数据的写入;

至于POLLPRI,后面的解释是作为紧急选项来设置的,在TCP协议报文中有个URG的紧急指针是表示先从紧急数据的地方开始读取,这里也是这个意思;

与select不同的是,reads与writes是输入输出参数,我上一篇博客中设置中:

 else if(fds[i] > 0 &&/
                                FD_ISSET(fds[i],&reads))    //正常事件,但是是非监听时间,也就代表时新建立的new_sock。
                                {
                                //  char buf[1024];
                                    ssize_t s = read(fds[i],buf,sizeof(buf) -1);
                                    if(s > 0)
                                    {
                                        buf[s] = '/0';
                                    //  printf("client : %s/n",buf);
                                        printf("client : %s",buf);
                                        FD_SET(fds[i],&writes);
                                    //  write(fds[i],buf,sizeof(s)+1);
                                    }
                                    else if(s == 0)
                                    {
                                        printf("client quit.../n");
                                        close(fds[i]);
                                        fds[i] = -1;
                                    }
                                    else{}
                                }
                        else{}
 
                        if(fds[i] > 0&&/
                                FD_ISSET(fds[i],&writes))
                        {
                            write(fds[i],buf,sizeof(buf));
                        }

在获取读事件时对将fds[i],设置到写事件中,而在poll中,读事件判断中修改设置为写事件。需要等待下一次循环,才能够获取到写事件,然后还必须将events属性在设置回来。

poll与select不同在于描述符存储方式不同和参数类型不同。

1.结构体数组的管理:当每次有需要关心的描述符时,将其放入结构体中,每次有无效的描述符后,将其描述符置-1,下次poll函数会忽略它。当有新的描述符加入时,从头遍历结构体,将为-1的元素设为要关心的描述符事件状态。切记:当新的描述符加到结构体数组末尾时要更新关心描述符个数,即poll第二个参数。

2.每次调用poll后,结构体元素revents会存储就绪事件状态,当每次重新调用poll之前时,系统会自己设置其为0,重新监听关心事件(不需要用户重新置0)

3.poll中参数不是输入,输出型,因此timeout,struct pollfd *fds参数不需重置,nfds看情况(参照第一点),而select函数是输入输出类型,每次调用前需重置。

下面看服务端的代码,客户端就不写了:

#include<poll.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define _BACKLOG_ 5
#define _SIZE_ 64

static void usage (const char *proc)
{
	printf("%s [ip][prot]/n",proc);
}
 
static int start(char *ip,int port)	
{
	int sock = socket(AF_INET,SOCK_STREAM,0);
	if(sock < 0)
	{
		perror("socket");
		exit(1);
	}
	
	int opt = 1;
	if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)) < 0)
	{
		perror("setsockopt");
		exit(3);
	}

	struct sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_port = htons(port);
	local.sin_addr.s_addr = inet_addr(ip);

	

	if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
	{
		perror("bind");
		exit(2);
	}

	if(listen(sock,_BACKLOG_) < 0)
	{
		perror("listen");
		exit(2);
	}
	return sock;
}



int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		usage(argv[0]);
		return 1;
	}

	int port = atoi(argv[2]);
	char *ip = argv[1];

	int listen_sock = start(ip,port);

	//pollfd arrays
	struct pollfd polls[_SIZE_];
	int index = 0;
	int timeout = 5000; //check question time
	int i = 0;
	polls[0].fd = listen_sock;
	polls[0].events = POLLIN;
	polls[0].revents = 0;
	++index;
	//init polls.fd
	for(i = 1;i < _SIZE_;++i)
	{
		polls[i].fd = -1;
	}
	char buf [1024];
	ssize_t s = 0;
	struct sockaddr_in client;
	socklen_t len = sizeof(client);
	int done = 0;
	int max_fd = 1;
	while(!done)
	{
		switch(poll(polls,max_fd,timeout))
		{
			case -1:
				perror("poll");
				break;
			case 0:
				printf("timeout/n");
				break;
			default:
				{
					size_t i = 0;
					for(;i<_SIZE_;++i)
					{
						if((polls[i].fd == listen_sock) && (polls[i].revents == POLLIN))
						{
							int accept_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
							if(accept_sock < 0)
							{
								perror("accept");
								continue;
							}
							printf("connet success/n");
							for(;i < _SIZE_;++i)
							{
								if(polls[i].fd == -1)
								{
									polls[i].fd = accept_sock;
									polls[i].events = POLLIN;
									++max_fd;
									break;
								}
							}
							if(i == _SIZE_)
							{
								close(accept_sock);
							}
						}
						else if((polls[i].fd > 0) &&(polls[i].revents == POLLIN) )
						{
							ssize_t size = read(polls[i].fd,buf,sizeof(buf)-1);
							if(size < 0)
							{
								perror("read");
								continue;
							}
							else if(size == 0)
							{
								printf("client close /n");
								struct pollfd tmp = polls[i];
								polls[i] = polls[max_fd -1];
								polls[max_fd -1] = tmp;

								close(polls[max_fd - 1].fd);
								polls[max_fd - 1].fd = -1;
							}
							else
							{
								buf[size] = '/0';
								printf("client # %s",buf);
								polls[i].events = POLLOUT;
							}
						}
						else if(polls[i].fd > 0&& /
								polls[i].revents == POLLOUT)
						{
							write(polls[i].fd,buf,sizeof(buf));
							polls[i].events = POLLIN;
						}
					}
				}
				break;
		}
	}
	return 0;
}

运行结果:

socket编程中poll怎么使用

到此,关于“socket编程中poll怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

原创文章,作者:506227337,如若转载,请注明出处:https://blog.ytso.com/222131.html

(0)
上一篇 2022年1月4日
下一篇 2022年1月4日

相关推荐

发表回复

登录后才能评论