Poll系统调用

poll系统调用和select类似,也是在指定时间内轮询一定数量的文件描述符,以 测试其是否有就绪者。

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

struct pollfd
{
   
	int fd;				//文件描述符
	short events;		//注册的事件
	short revents;		//实际发生的事件,由内核填充
};
  • fds参数是一个pollfd类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。
    其中fd成员指定文件描述符;events成员告诉poll监听fd上的哪些事件,它是一系列事件的按位或;revents成员则由内核修改,以通知应用程序fd上实际发生了哪些事件。
  • poll事件类型
  • nfds参数指定了被监听事件集合fds的大小。
   typedef unsigned long int nfds_t;
  • timeout则指定poll的超时值

poll编程实例

poll编程流程

/* * 1.创建socket-->bind-->listen * 2.创建一个数组存储socketfd和事件 并初始化数组 * 3.先将servicefd放入数组 * 4.循环:① 创建poll,然后处理连接 * ② 如果是新连接,放入数组并注册事件 * ③ 如果断开就从中删除 * ④ 都不是就收发数据 */

头文件tcp_socket.h


#include<iostream>
#include<assert.h>
#include<string.h>

#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

#include<unistd.h>

#define BUFF_SIZE 128

using namespace std;
class Socket
{
   
public:
	Socket()
	{
   
		sockfd_ = socket(PF_INET, SOCK_STREAM, 0);
		assert(sockfd_ >= 0);
	}

	int Get_socket()
	{
   
		return sockfd_;
	}

	~Socket()
	{
   
		close(sockfd_);
	}
protected:
	int sockfd_;
};

class Socket_Ser :public Socket
{
   
public:
	Socket_Ser(const char* ip, int port = 6000, int backlog = 5)
	{
   
		struct sockaddr_in address;
		memset(&address, 0, sizeof(address));

		address.sin_family = AF_INET;
		address.sin_port = htons(port);
		address.sin_addr.s_addr = inet_addr(ip);

		int ret = bind(sockfd_, (struct sockaddr*)&address, sizeof(address));
		assert(ret != -1);

		ret = listen(sockfd_, backlog);
		assert(ret != -1);
	}

	int Accept()
	{
   
		struct sockaddr_in client;
		socklen_t len = sizeof(client);
		int connfd = accept(sockfd_, (struct sockaddr*)&client, &len);
		return connfd;
	}

	int Recv(int fd, char* buffer, int size)
	{
   
		int ret = recv(fd, buffer, size - 1, 0);
		if (ret == -1 || (strncmp(buffer, "end", 3) == 0))
		{
   
			return -1;
		}
	}

	void Send(int fd, const char* buffer, int size)
	{
   
		send(fd, buffer, size, 0);
	}

	void Close_client(int fd)
	{
   
		close(fd);
	}
};

class Socket_Cli :public Socket
{
   
public:
	Socket_Cli(const char* ip, int port = 6000)
	{
   
		struct sockaddr_in address;
		memset(&address, 0, sizeof(address));

		address.sin_family = AF_INET;
		address.sin_port = htons(port);
		address.sin_addr.s_addr = inet_addr(ip);

		int ret = connect(sockfd_, (struct sockaddr*)&address, sizeof(address));
		assert(ret != -1);
	}

	int Send(char* buffer, int size)
	{
   
		send(sockfd_, buffer, size, 0);
	}
};

头文件tcp_poll.h

#include"tcp_socket.h"
#include <poll.h>
#include <list>

#define PollSize 100
class Poll
{
   
public:
	Poll(const char* ip, int port = 6000, int backlog = 5) :ser(ip, port, backlog)
	{
   	
		for (int i = 0; i < PollSize; i++)		//数组初始化
		{
   
			cli_arr[i].fd = -1;
			cli_arr[i].events = 0;
		}
		Insert(ser.Get_socket(), POLLIN);
	}

public:
	void Deal()
	{
   
		UsingPoll();
		Deal_connect();
	}
private:
	void Insert(int fd, short events)
	{
   
		int i = 0;
		for (; i < PollSize; i++)
		{
   
			if (cli_arr[i].fd == -1)	//找到一个未用过的数组
			{
   
				cli_arr[i].fd = fd;
				cli_arr[i].events = events;
				break;
			}
		}
		if (i == PollSize) cout << "client array is full!" << endl;
	}

	void Delete(struct pollfd& PollFd)
	{
   
		ser.Close_client(PollFd.fd);
		PollFd.fd = -1;
		PollFd.events = 0;
	}

	void Deal_connect()
	{
   
		for (int i = 0; i < PollSize; i++)
		{
   
			if (cli_arr[i].fd == ser.Get_socket())		//服务器端
			{
   
				if (cli_arr[i].revents & POLLIN)	//新的连接请求
				{
   
					int connfd = ser.Accept();
					cout << "client " << connfd << "link" << endl;

					Insert(connfd, POLLIN | POLLRDHUP);		//注册
				}
			}
			else					//客户端发送数据
			{
   
				if (cli_arr[i].revents & POLLRDHUP)		//客户端断开
				{
   
					cout << "client " << cli_arr[i].fd << "unlink" << endl;
					Delete(cli_arr[i]);
				}
				else if (cli_arr[i].revents & POLLIN)	//客户端发送数据
				{
   
					char buffer[128] = {
    0 };
					ser.Recv(cli_arr[i].fd, buffer, 128);
					cout << "recv form " << cli_arr[i].fd << ":" << buffer << endl;
				}
			}
		}
	}

	int UsingPoll()
	{
   
		int n = poll(cli_arr, PollSize, -1);
		if (n <= 0)
		{
   
			cout << "poll error" << endl;
			return -1;
		}
	}
private:
	Socket_Ser ser;

	struct pollfd cli_arr[PollSize];
};

主文件tcp_poll.cpp

#include "tcp_poll.h"

int main(int argc, char* argv[])
{
   
	if (argc <= 1)
	{
   
		cout << "errno! please input again" << endl;
	}
	Poll mypoll(argv[1]);

	while (1)
	{
   
		mypoll.Deal();
	}
}

参考文献

[1]游双.Linux高性能服务器编程.机械工业出版社,2043.5.