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