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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章