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