C++网络编程之poll

       poll机制与select机制类似,通过管理文件描述符来进行轮询,效率更高,并且处理的连接个数不受内核的限制。

1、poll函数

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

参数:

(1)fdarray:可读套接字,是一个指向数组的指针,这个数组是由 struct pollfd 作为元素构成的,pollfd结构体:

struct pollfd {
    int fd;         		// 用于检测的文件描述符
    short events;         // 等待的事件类型
    short revents;        // 实际发生的事件类型
} ; 

(2)nfds:所监控的最大文件描述符的个数,使用的时候传入当前最大的文件描述符号+1 即可 。

(3)timeout:工作模式:

阻塞模式

        将 timeout = INFTIM传入即可,当代码执行poll 函数的所在行的时候,若是fdarray 数组中的所有套接字描述符上面都没有发生变化,则代码不会向下执行,而是卡在 poll 所在行,直到 fdarray 中的套接字描述符有发生变化poll 方法才会返回发生变化的全部套接字的个数,并继续向下执行;若出错则返回-1 。

非阻塞模式

       将 timeout = 0传入即可,当代码执行到 poll 函数所在行的时候,若是 fdarray 数组中的所有套接字均没有变化,则不作停留,立即返回0; 若是 fdarray数组中存在套接字发生变化的,则立即返回发生变化的套接字的总数;若是 poll内部出错,立即返回 -1 。

固定时间内阻塞模式

       将 timeout 设置为非零的整数,当代名执行到 poll 函数所在行的时候,会等待 timeout 秒,在时间到达的时候,返回在这一段时间内发生变化的套接字总个数(没有就返回 0);若是在 timeout(sec) 这段时间内有错误发生的话,立即返回 -1 。

(4)返回值:

  • -1       : poll 执行过程中出错;
  • 0         : 没有任何套接字上没有变化;
  • n(n>0)  :有 n 个套接字上面有变化(来可读数据,有了可写的数据) 。

例:

服务端

Server.h

#pragma once

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include "error.h"

using namespace std;

#define BACKLOG 5

class Server
{
public:
	Server(int iPort = 5000);
	~Server();
	// 初始化可读套接字
	void initReadfds();
	// 获取最大描述符
	int getMaxFd();
	// 添加客户端到可读套接字
	void addClient(int c);
	// 移除客户端套接字
	void removeClient(int c);

	void pollRun();

private:
	int port;		// 端口
	int listenfd;	// 监听套接字
	int clientfd;	// 客户端套接字
	int maxFd;		// 套接字最大描述符

	struct sockaddr_in clientaddr;	// 客户端地址结构体
	struct sockaddr_in listenaddr;	// 监听服务端地址结构体
	struct pollfd readfds[BACKLOG]; // poll套接字结构体
	socklen_t clientaddrlen;		// 客户端地址长度
};

Server.cpp

#include "Server.h"

Server::Server(int iPort) : port(iPort), listenfd(-1), clientfd(-1)
{
	listenaddr.sin_family = AF_INET;							// 初始化监听套接字协议簇为AF_INET
	listenaddr.sin_port = htons(port);							// 初始化监听套接字端口号
	listenaddr.sin_addr.s_addr = htonl(INADDR_ANY);				// 初始化监听套接字地址
	listenfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);	// 创建套接字,设置为非阻塞的方式
	int bind_val = bind(listenfd, (struct sockaddr*)&listenaddr, sizeof(listenaddr));	// 绑定套接字
	if (bind_val < 0) {
		cout << "bind error!" << endl;
	}
	int listen_val = listen(listenfd, BACKLOG);	// 监听套接字
	if (listen_val < 0) {
		cout << "listen error!" << endl;
	}
}

Server::~Server()
{
	// 关闭所有套接字
	for (int i = 0; i < BACKLOG; i++){
		close(readfds[i].fd);
	}

	cout << "close server" << endl;
}

void Server::initReadfds()
{
	for (int i = 0; i < BACKLOG; i++) {
		readfds[i].fd = -1;
		readfds[i].events = POLLIN;
	}
	readfds[listenfd].fd = listenfd;
	readfds[listenfd].events = POLLIN;
}

int Server::getMaxFd()
{
	maxFd = -1;
	for (int i = 0; i < BACKLOG; i++) {
		if (readfds[i].fd > maxFd) {
			maxFd = readfds[i].fd;
		}
	}
	return maxFd;
}

void Server::addClient(int c)
{
	readfds[c].fd = c;
	readfds[c].events = POLLIN;
}

void Server::removeClient(int c)
{
	readfds[c].fd = -1;
}

void Server::pollRun()
{
	initReadfds();
	while (true) {
		maxFd = getMaxFd();
		// 检测各个套接字信号变化
		int poll_val = poll(readfds, maxFd + 1, 5);
		for (int i = 0; i < BACKLOG; i++) {
			cout << i << " readfds.fd = " << readfds[i].fd << endl;
			if (readfds[i].fd < 0) {
				continue;
			}
			clientaddrlen = sizeof(struct sockaddr_in);
			clientfd = accept(readfds[i].fd, (struct sockaddr*)&clientaddr, &clientaddrlen);
			if (-1 == clientfd) {
				cout << "client connect error!" << endl;
				sleep(3);
				continue;
			}
			else if (0 == clientfd) {
				cout << "client connect close!" << endl;
				continue;
			}
			else {
				cout << "client connect success!" << endl;
				char data[1024];
				memset(data, 0, sizeof(data));
				int recv_val = recv(clientfd, data, sizeof(data), 0);	// 注意此处的句柄为客户端句柄
				if (-1 == recv_val) {
					cout << "received error, continue, recv_val = " << recv_val << endl;
					// Sleep(3);
					continue;
				}
				else if (0 == recv_val) {
					cout << "client connect close, remove, recv_val = " << recv_val << endl;
					removeClient(readfds[i].fd);
					continue;
				}
				else {
					cout << "recv_val = " << recv_val << ", data = " << data << endl;
				}
				continue;
			}
		}
	}
}

注意:recv()的第一个参数应该为客户端的句柄clientfd。

main.cpp

#include <iostream> 
#include "Server.h"

using namespace std;

int main()
{
	int port = 5666;
	Server *s = new Server(port);
	try	{
		s->pollRun();
	}
	catch (const char* e) {
		cout << "e = " << e << endl;
	}

	delete s;

	return 0;
}

客户端:

Client.h

#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <cstring>
#include <cstdio>
#include <signal.h>
#include <unistd.h>

#define MAX_SIZE 100
using namespace std;

class Client
{
public:
	Client(int iport = 5000);
	~Client();
	void run();

private:
	int port;
	int serverFd;
	struct sockaddr_in serverAddr;


};

Client.cpp

#include "Client.h"

Client::Client(int iport) : port(iport), serverFd(-1)
{
	serverFd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == serverFd) {
		throw("create socket error!");
	}
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(port);
	// inet_pton(AF_INET, "10.206.142.12", &serverAddr.sin_addr.s_addr);
	inet_pton(AF_INET, "0.0.0.0", &serverAddr.sin_addr.s_addr);
	int ret;
	ret = connect(serverFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
	if (ret < 0) {
		throw("connect server error!");
	}
}

Client::~Client()
{
	cout << "close client! " << endl;
	if (-1 == serverFd) {
		close(serverFd);
	}
}

void Client::run()
{
	while (true) {
		char  data[MAX_SIZE];
		int ret;
		memcpy(data, "hello", sizeof("hello"));
		ret = send(serverFd, data, sizeof(data), 0);
		if (ret < 0) {
			cout << "send error!" << endl;
		}
		else {
			cout << "send success, data = " << data << endl;
		}
		sleep(3);
	}	
}

main.cpp

#include <iostream>
#include "Client.h"

using namespace std;
int main()
{
	try {	
		int port = 5666;
		Client * c = new Client(port);
		c->run();		
		delete c;
	}
	catch (const char* e) {
		cout << "e = " << e << endl;
	}

	return 0;
}
原创文章 99 获赞 68 访问量 3万+

猜你喜欢

转载自blog.csdn.net/King_weng/article/details/103937451