当前位置: 首页 > news >正文

C++网络编程之IO多路复用(二)

概述

        在上一篇文章中,我们介绍了如何使用select进行IO多路复用。虽然select在很多场景下非常有用,但它存在线性扫描、复制文件描述符集合、不支持边缘触发模式、信号干扰等众多问题。因此,在更高效的IO多路复用方案中,往往会选择poll和epoll。在本篇中,我们将重点介绍poll,下一篇将介绍epoll。

poll

        poll是对select的一个改进版本,它不再受固定数量限制的影响,而是采用动态数组来存储待监测的文件描述符列表。此外,poll还能更好地处理不同类型的数据流,比如:普通文件、管道等。

        poll函数的接口原型如下。

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

        各个参数和返回值的含义如下。

        fds: 指向struct pollfd结构体数组的指针,每个元素代表一个要监视的文件描述符及其感兴趣的事件。pollfd的定义如下,其events和revents字段可以包含以下常量的组合:POLLIN(有数据可读)、POLLOUT(可写入数据)、POLLERR(发生错误)、POLLHUP(挂起事件)、POLLNVAL(无效请求)。

struct pollfd
{int   fd;         // 文件描述符short events;     // 请求的事件short revents;    // 发生的事件
};

        nfds:fds数组中的元素数量。

        timeout:等待的时间上限(毫秒)。如果设置为-1,则无限期等待;如果为0,则立即返回不等待。

        返回值:大于0表示准备好执行IO操作的文件描述符的数量;0表示超时;-1表示出错,可以通过errno查看具体的错误码。

        注意:poll仍然需要遍历整个文件描述符列表来找出已经就绪的项,性能问题依旧存在。另外,还需要在用户空间与内核空间之间复制pollfd结构体,增加了一定的开销。

实战代码

        虽然poll是Linux系统才有的API,但在Windows系统中,我们可以使用WSAPoll函数来实现类似的功能。在下面的示例代码中,我们使用WSAPoll函数实现了TCP服务器的IO多路复用。

        首先,我们使用WSAStartup初始化Winsock库,并检查是否成功。接着,创建一个监听套接字,将其设置为非阻塞模式。同时,将其绑定到指定的端口8888,并调用listen开始监听连接请求。

        然后,我们使用变量vctFds来存储所有需要监控的套接字,包括:监听套接字、所有已连接的客户端套接字。

        紧接着,在无限循环中,我们使用WSAPoll函数来等待IO事件的发生。如果WSAPoll返回新的连接请求,接受新连接,并将新连接的套接字添加到vctFds向量中。对于每个已连接的客户端套接字,检查是否有数据可读。如果有,则读取数据并回显给客户端。如果客户端断开连接或发生错误,则关闭相应的套接字,并从vctFds中移除该套接字。

        最后,当程序退出时,关闭所有打开的套接字,并清理Winsock库。

#include <winsock2.h>
#include <iostream>
#include <vector>using namespace std;#pragma comment(lib, "ws2_32.lib")int main()
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){cout << "Failed to initialize Winsock" << endl;return -1;}SOCKET listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (listenSock == INVALID_SOCKET){cout << "Create socket failed: " << WSAGetLastError() << endl;WSACleanup();return -1;}u_long mode = 1;ioctlsocket(listenSock, FIONBIO, &mode);sockaddr_in serverAddr;memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(8888);serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listenSock, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR){cout << "Bind failed: " << WSAGetLastError() << endl;closesocket(listenSock);WSACleanup();return -1;}if (listen(listenSock, SOMAXCONN) == SOCKET_ERROR){cout << "Listen failed: " << WSAGetLastError() << endl;closesocket(listenSock);WSACleanup();return -1;}vector<WSAPOLLFD> vctFds;vctFds.push_back({ listenSock, POLLIN, 0 });while (true){int result = WSAPoll(vctFds.data(), static_cast<ULONG>(vctFds.size()), -1);if (result == SOCKET_ERROR){cout << "WSAPoll failed: " << WSAGetLastError() << endl;break;}for (size_t i = 0; i < vctFds.size(); ++i){if (vctFds[i].revents & POLLIN){if (vctFds[i].fd == listenSock) {sockaddr_in clientAddr;int addrLen = sizeof(clientAddr);SOCKET connSock = accept(listenSock, (SOCKADDR *)&clientAddr, &addrLen);if (connSock != INVALID_SOCKET) {vctFds.push_back({ connSock, POLLIN, 0 });cout << "New connection" << endl;}}else{char buffer[1024] = { 0 };int bytesReceived = recv(vctFds[i].fd, buffer, sizeof(buffer) - 1, 0);if (bytesReceived > 0){cout << "Message from client: " << buffer << endl;send(vctFds[i].fd, buffer, bytesReceived, 0);}else if (bytesReceived == 0 || (bytesReceived == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)){cout << "Client disconnected" << endl;closesocket(vctFds[i].fd);vctFds.erase(vctFds.begin() + i);--i;}}}else if (vctFds[i].revents & (POLLERR | POLLHUP | POLLNVAL)){cout << "Error on fd " << vctFds[i].fd << ": ";if (vctFds[i].revents & POLLERR){cout << "POLLERR ";}if (vctFds[i].revents & POLLHUP){cout << "POLLHUP ";}if (vctFds[i].revents & POLLNVAL){cout << "POLLNVAL ";}cout << endl;closesocket(vctFds[i].fd);vctFds.erase(vctFds.begin() + i);--i;}}}for (auto &fd : vctFds){if (fd.fd != INVALID_SOCKET){closesocket(fd.fd);}}closesocket(listenSock);WSACleanup();return 0;
}


http://www.mrgr.cn/news/68248.html

相关文章:

  • HCIP--3实验- 链路聚合,VLAN间通讯,Super VLAN,MSTP,VRRPip配置,OSPF(静态路由,环回,缺省,空接口),NAT
  • jmeter常用配置元件介绍总结之取样器
  • 微服务系列一:基础拆分实践
  • 父组件调用函数式子组件,并向子组件传递函数参数。
  • vue中实现列表无缝动态滚动
  • 主格、宾格、形容词性物主代词和名词性物主代词
  • 中阳智能模型的量化投资创新及前景
  • 解决windows server 2016 安装IIS失败问题
  • 【Linux系统编程】第四十三弹---多线程编程指南:线程终止方式与C++11中的thread
  • Vue3+element-plus摘要
  • 全局池化(Global Pooling)
  • css基础:底部固定,导航栏浮动在顶部
  • MyBatis项目的创建和增删查改操作
  • Python中的多线程效率分析
  • metrics
  • 数据库数据恢复—通过拼接数据库碎片恢复 SQL Server 数据库
  • 计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
  • spark-本地模式的配置和简单使用
  • MQTT应用实例:Air780E模组AT指令的实践!
  • vue2 关闭 Uncaught error 的全屏提示
  • LabVIEW实验室液压制动系统
  • 【Unity】ScriptableObject的应用和3D物体跟随鼠标移动:鼠标放置物体在场景中
  • 无惧任天堂的法律威胁:Switch模拟器Ryujinx v1.2.72版发布
  • C#编程基础:静态成员与实例成员的区别及访问方式
  • 27系统日志查看
  • 【含开题报告+项目源码+免费部署】基于SSM的医院挂号住院系统的设计与实现