网络I/O模型

网络I/O模型

  • 同步I/O
    • 阻塞I/O
    • 非阻塞I/O
    • I/O多路复用
      • select
        • 函数接口
        • 示例
      • poll
        • 函数接口
        • 示例
      • poll 和 select 的区别
      • epoll
        • 原理:
        • 示例
  • 异步I/O

同步I/O

阻塞I/O

Alt

一个基本的C/S模型如下图所图:其中 listen()、connect()、write()、read() 都是阻塞I/O,用户态的进程在执行这些操作时会陷入内核态并被挂起。直到 socket() 上有数据可读/写时,进程才会从内核态切换到用户态,并完成数据在内核态和用户态之间的复制。

int fd = open("file.txt", O_RDONLY);
char buffer[100];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer));	// 阻塞
close(fd);

非阻塞I/O

Alt

I/O多路复用

select

select() 函数是一个用于多路复用I/O的系统调用,允许程序同时监控多个文件描述符(如套接字、管道、终端等)的可读、可写或异常状态。

函数接口
#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

其中:

  • nfds: 整型变量,表示在所有集合中最大的文件描述符加1。这是为了告诉select()需要检查的文件描述符范围。实际上,你只需要提供所有被监控的文件描述符中的最大值加1即可。
  • readfds: 指向fd_set结构的指针,用于存放待检查可读性的文件描述符集合。如果某个文件描述符在此集合中并且变为可读,select()返回后,该文件描述符在集合中的状态仍会被保持为已设置。
  • writefds: 用于存放待检查可写性的文件描述符集合。
  • exceptfds: 用于存放待检查异常条件的文件描述符集合。
  • timeout: 指向timeval结构的指针,用来指定select()调用的最大等待时间。如果为NULL,select()将一直阻塞直到有文件描述符满足条件;如果不为NULL且值为(0, 0),则select()会立即返回,不进行等待;如果指定了具体的时间,则select()最多等待指定的时间。

返回值

  • 正值: 返回的是已就绪的文件描述符的数量(包括可读、可写或异常的)。注意,这个值并不直接告诉你哪些描述符就绪,你需要通过FD_ISSET宏检查集合来确定具体哪些描述符准备好了。

  • 0: 表示在指定的超时时间内没有文件描述符变为就绪状态。

  • -1: 表示调用出错,此时可以查看errno来获取具体的错误信息。

示例
void updateReadSet(std::unordered_set<int> &clientFds, int &maxFd, int sockFd, fd_set &readSet) {
  maxFd = sockFd;
  FD_ZERO(&readSet);
  FD_SET(sockFd, &readSet);
  for (const auto &clientFd : clientFds) {
    if (clientFd > maxFd) {
      maxFd = clientFd;
    }
    FD_SET(clientFd, &readSet);
  }
}

void handlerClient(int clientFd) {
  std::string msg;
  if (not EchoServer::RecvMsg(clientFd, msg)) {
    return;
  }
  EchoServer::SendMsg(clientFd, msg);
}

int main(int argc, char *argv[]) {
  if (argc != 3) {
    std::cout << "invalid input" << std::endl;
    std::cout << "example: ./Select 0.0.0.0 1688" << std::endl;
    return -1;
  }
  int sockFd = EchoServer::CreateListenSocket(argv[1], atoi(argv[2]), false);
  if (sockFd < 0) {
    return -1;
  }
  int maxFd;
  fd_set readSet;
  EchoServer::SetNotBlock(sockFd);
  std::unordered_set<int> clientFds;
  while (true) {
    updateReadSet(clientFds, maxFd, sockFd, readSet);
    int ret = select(maxFd + 1, &readSet, NULL, NULL, NULL);
    if (ret <= 0) {
      if (ret < 0) perror("select failed");
      continue;
    }
    for (int i = 0; i <= maxFd; i++) {
      if (not FD_ISSET(i, &readSet)) {
        continue;
      }
      if (i == sockFd) {  // 监听的sockFd可读,则表示有新的链接
        EchoServer::LoopAccept(sockFd, 1024, [&clientFds](int clientFd) {
          clientFds.insert(clientFd);  // 新增到要监听的fd集合中
        });
        continue;
      }
      handlerClient(i);
      clientFds.erase(i);
      close(i);
    }
  }
  return 0;
}


poll

在 select() 中文件描述符集合是由一个大小为1024的位图实现的,为了支持监听更多的文件描述符,poll 结构体数组存放描述符集合。

struct pollfd {
	int   fd;		// 文件描述符
	short events;	// 监听的事件
	short revents;	// 返回的事件
}
函数接口
#include <poll.h>
// nfds 指明fds数组的大小
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
示例
void updateFds(std::unordered_set<int> &clientFds, pollfd **fds, int &nfds) {
  if (*fds != nullptr) {
    delete[](*fds);
  }
  nfds = clientFds.size();
  *fds = new pollfd[nfds];
  int index = 0;
  for (const auto &clientFd : clientFds) {
    (*fds)[index].fd = clientFd;
    (*fds)[index].events = POLLIN;
    (*fds)[index].revents = 0;
    index++;
  }
}

void handlerClient(int clientFd) {
  std::string msg;
  if (not EchoServer::RecvMsg(clientFd, msg)) {
    return;
  }
  EchoServer::SendMsg(clientFd, msg);
}

int main(int argc, char *argv[]) {
  if (argc != 3) {
    std::cout << "invalid input" << std::endl;
    std::cout << "example: ./Poll 0.0.0.0 1688" << std::endl;
    return -1;
  }
  int sockFd = EchoServer::CreateListenSocket(argv[1], atoi(argv[2]), false);
  if (sockFd < 0) {
    return -1;
  }
  int nfds = 0;
  pollfd *fds = nullptr;
  std::unordered_set<int> clientFds;
  clientFds.insert(sockFd);
  EchoServer::SetNotBlock(sockFd);
  while (true) {
    updateFds(clientFds, &fds, nfds);
    int ret = poll(fds, nfds, -1);
    if (ret <= 0) {
      if (ret < 0) perror("poll failed");
      continue;
    }
    for (int i = 0; i < nfds; i++) {
      if (not(fds[i].revents & POLLIN)) {
        continue;
      }
      int curFd = fds[i].fd;
      if (curFd == sockFd) {
        EchoServer::LoopAccept(sockFd, 1024, [&clientFds](int clientFd) {
          clientFds.insert(clientFd);  // 新增到要监听的fd集合中
        });
        continue;
      }
      handlerClient(curFd);
      clientFds.erase(curFd);
      close(curFd);
    }
  }
  return 0;
}

poll 和 select 的区别

  1. 数据结构和参数
  • select: 使用固定大小的位图(fd_set)来表示文件描述符集合,上限为1024.
  • poll: 使用动态数组(pollfd 结构体数组)来表示文件描述符集合。
  1. 性能
  • select: 每次调用时,用户态的文件描述符集合需要复制到内核态。
  • poll: 通过传递指针的方式避免了每次调用时复制整个文件描述符集合到内核。

epoll

原理:
  • 事件注册:使用epoll_create创建一个epoll文件描述符,然后通过epoll_ctl向这个文件描述符注册事件(如读、写、错误等)和对应的socket描述符。

  • 事件等待:通过epoll_wait函数等待一个或多个事件的发生。这个调用是阻塞的,直到至少有一个已注册的事件发生,或者超时,或者被中断。相比于select和poll,epoll_wait的优势在于它不会随着监听的文件描述符数量增加而导致效率下降,因为它内部维护了一个高效的红黑树结构来管理这些描述符。

  • 事件处理:当epoll_wait返回时,会给出一个就绪事件的列表,应用可以直接对这些事件进行处理,而无需遍历所有监控的文件描述符

水平触发和边缘触发:

  • LT模式下,只要事件未被处理,每次调用epoll_wait都会返回该事件。
  • ET模式下,事件仅在状态发生变化的那一刻返回一次,要求应用程序一次性处理完所有就绪的数据,否则可能会丢失事件。
示例
void handlerClient(int clientFd) {
  std::string msg;
  if (not EchoServer::RecvMsg(clientFd, msg)) {
    return;
  }
  EchoServer::SendMsg(clientFd, msg);
}

int main(int argc, char *argv[]) {
  if (argc != 3) {
    std::cout << "invalid input" << std::endl;
    std::cout << "example: ./Epoll 0.0.0.0 1688" << std::endl;
    return -1;
  }
  int sockFd = EchoServer::CreateListenSocket(argv[1], atoi(argv[2]), false);
  if (sockFd < 0) {
    return -1;
  }
  epoll_event events[2048];
  int epollFd = epoll_create(1024);
  if (epollFd < 0) {
    perror("epoll_create failed");
    return -1;
  }
  EchoServer::Conn conn(sockFd, epollFd, false);
  EchoServer::SetNotBlock(sockFd);
  EchoServer::AddReadEvent(&conn);
  while (true) {
    int num = epoll_wait(epollFd, events, 2048, -1);
    if (num < 0) {
      perror("epoll_wait failed");
      continue;
    }
    for (int i = 0; i < num; i++) {
      EchoServer::Conn *conn = (EchoServer::Conn *)events[i].data.ptr;
      if (conn->Fd() == sockFd) {
        EchoServer::LoopAccept(sockFd, 2048, [epollFd](int clientFd) {
          EchoServer::Conn *conn = new EchoServer::Conn(clientFd, epollFd, false);
          EchoServer::AddReadEvent(conn);                 // 监听可读事件,保持fd为阻塞IO
          EchoServer::SetTimeOut(conn->Fd(), 0, 500000);  // 设置读写超时时间为500ms
        });
        continue;
      }
      handlerClient(conn->Fd());
      EchoServer::ClearEvent(conn);
      delete conn;
    }
  }
  return 0;
}

异步I/O

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/670043.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java面试题:Redis2_解决Redis缓存击穿问题

缓存击穿 当一个key过期时,需要对这个key进行数据重建 在重建的时间内如果有大量的并发请求进入,就会绕过缓存进入数据库,会瞬间击垮DB 重建时间可能因为数据是多个表的混合结果需要分头统计而延长,从而更容易出现缓存击穿问题 缓存击穿的解决方案 添加互斥锁 先查询缓存…

电商商城管理系统

前言&#x1f440;~ 将近一个月没更新了&#xff0c;最近忙着学校的大作业&#xff0c;一个是微信小程序的、一个是互联网编程的&#xff0c;也是忙完了这个大作业&#xff0c;这个大作业前端使用了vue、后端使用了java&#xff0c;接下来展示一些效果图&#xff0c;如果有需要…

2024 第三届 AIGC 中国开发者大会:多模态大模型的发展与趋势

引言 在2024年第三届AIGC中国开发者大会上&#xff0c;零一万物联合创始人潘欣分享了多模态大模型的发展与趋势。潘欣对多模态大模型的历史、现状和未来进行了详细回顾和深刻思考&#xff0c;为我们揭示了该领域的发展路径和技术前景。本文将详细解读潘欣的分享内容&#xff0…

如何搜索[仅有1个文件]或[指定个数范围、名称、类型文件等复杂情况]的文件夹

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 打开工具&#xff0c;切换到批量复制板块&#xff0c;用Ctrl5可以快速切换 然后鼠标移动到工具的贴边的右侧&#xff0c;不是移出工具外面&#xff0c;还在…

RT_thread nano移植Finsh

参考连接: https://blog.csdn.net/baseball214/article/details/131341722 移植的前提是,你已经有一个可以使用的nano功能. 1.将rtthread-nano-master\rt-thread\components文件复制到工程. 2.添加Finsh中的.c以及相关.h头文件路径 3.注释掉finsh_config.h文件中以下两个宏…

颠仆流离学二叉树2 (Java篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

使用autodl服务器进行模型训练

1.注册并且选择一个服务器租用 2.点击jupyter lab进入服务器内部 3.把yolov5-master这个的压缩文件上传到jupyter的文件列表中 4.打开终端 (1)查看目录 ls (2)解压yolov5-master(1) unzip "yolov5-master (1).zip" 可以看到解压成功&#xff01; (3)进入yolov5-m…

网桥、路由器和网关有什么区别

在计算机网络领域&#xff0c;网桥、路由器和网关都是常见的网络设备&#xff0c;它们在网络通信中扮演着不同的角色。虽然它们都有连接不同网络的功能&#xff0c;但在实际应用中却具有各自独特的作用和特点。 1.网桥&#xff08;Bridge&#xff09; 定义&#xff1a;网桥是…

【云原生】Kubernetes----配置资源管理Secrets与ConfigMaps

目录 一、Secrets &#xff08;一&#xff09;Secrets概述 &#xff08;二&#xff09;Secrets类型 &#xff08;三&#xff09;Secrets使用方式 &#xff08;四&#xff09;创建Secrets 1.陈述式命令创建 1.1 定义用户与密码文件 1.2 使用陈述式命令创建 2.使用base6…

每日一题《leetcode--LCR 022.环形链表||》

https://leetcode.cn/problems/c32eOV/ 我们使用两个指针&#xff0c;fast 与 slow。它们起始都位于链表的头部。随后slow 指针每次向后移动一个位置&#xff0c;而fast 指针向后移动两个位置。如果链表中存在环&#xff0c;则fast 指针最终将再次与slow 指针在环中相遇。 stru…

飞腾D2000+FPGA云终端,实现从硬件、操作系统到应用的完全国产、自主、可控

飞腾云终端基于国产化飞腾高性能8核D2000处理器平台的国产自主可控解决方案&#xff0c;搭载昆仑国产化固件,支持UOS、银河麒麟等国产操作系统&#xff0c;满足国产化信息安全运算的需求&#xff0c;实现从硬件、操作系统到应用的完全国产、自主、可控&#xff0c;是国产信息安…

排序进阶----快速排序

当我们写了插入和希尔排序后&#xff0c;我们就应该搞更难的了吧。大家看名字就知道我们这篇博客的内容了吧。而且从名字上来看。快速排序就很快吧。那么为什么这个排序怎么能叫快速排序啊。我们希尔排序不是很快嘛。那么我们的快速排序肯定是有特殊之处嘞。不然这就太自负了。…

【简单讲解下Fine-tuning BERT,什么是Fine-tuning BERT?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

paddleocr快速入门:基于python脚本及命令行两种方式实现图片OCR识别

本篇将再讲讲paddleocr在图像OCR识别方面的应用。 一、paddlecor参数说明 字段说明默认值use_gpu是否使用GPUTRUEgpu_mem初始化占用的GPU内存大小8000Mimage_dir通过命令行调用时执行预测的图片或文件夹路径page_num当输入类型为pdf文件时有效&#xff0c;指定预测前面page_nu…

R语言ggplot2包绘制世界地图

数据和代码获取&#xff1a;请查看主页个人信息&#xff01;&#xff01;&#xff01; 1. 数据读取与处理 首先&#xff0c;从CSV文件中读取数据&#xff0c;并计算各国每日收入的平均签证成本。 library(tidyverse) ​ df <- read_csv("df.csv") %>% group_…

MAC帧

基本问题 数据链路层的协议有很多&#xff0c;但是都有三个基本问题&#xff1a;封装成帧&#xff0c;透明传输和差错检测。 封装成帧 封装成帧&#xff08;Framing&#xff09;就是在一段数据的前后分别添加首部和尾部&#xff0c;这样就构成了一个帧。帧是数据链路层的传送…

css 中clip 属性和替代方案 clip-path属性使用

clip clip 属性概述 作用&#xff1a;clip 属性用于定义一个裁剪区域&#xff0c;该区域外的元素内容将不可见。适用元素&#xff1a;clip 属性只对绝对定位&#xff08;position: absolute&#xff09;或固定定位&#xff08;position: fixed&#xff09;的元素有效&#xf…

掘金AI 商战宝典-高阶班:如何用AI制作视频(11节视频课)

课程目录&#xff1a; 1-第一讲用AI自动做视频&#xff08;上&#xff09;_1.mp4 2-第二讲用AI自动做视频&#xff08;中&#xff09;_1.mp4 3-第四讲A1做视频实战&#xff1a;店铺宣传_1.mp4 4-第五讲Al做视频实战&#xff1a;商品带贷1.mp4 5-第六讲Al做视频实战&#x…

码随想录算法训练营第二十四天| 77. 组合

77. 组合 - 力扣&#xff08;LeetCode&#xff09; class Solution {ArrayList<Integer> path new ArrayList<>();ArrayList<List<Integer>> result new ArrayList<>();public List<List<Integer>> combine(int n, int k) {if(n &…

SAP揭秘者- SAP PP模块日常常见运维问题之工单入库失败原因分析及快速处理

文章摘要&#xff1a; 无论您是负责SAP实施项目还是负责SAP运维项目&#xff0c;当用户发现有SAP PP模块的各种异常问题的时都需要作为SAP PP顾问的您快速地理解用户提交的问题&#xff0c;并快速地解决这些问题&#xff0c; 上篇文章跟大家聊了基本单位维护错了怎么修改的解决…