《TCP/IP网络编程》学习笔记 | Chapter 22:重叠 I/O 模型

《TCP/IP网络编程》学习笔记 | Chapter 22:重叠 I/O 模型

  • 《TCP/IP网络编程》学习笔记 | Chapter 22:重叠 I/O 模型
    • 理解重叠 I/O 模型
      • 重叠 I/O
      • 本章讨论的重叠 I/O 的重点不在于 I/O
    • 创建重叠 I/O 套接字
    • 执行重叠 I/O 的 WSASend 函数
    • 进行重叠 I/O 的 WSARecv 函数
    • 重叠 I/O 的 I/O 完成确认
      • 使用事件对象
      • 使用 Completion Routine 函数

《TCP/IP网络编程》学习笔记 | Chapter 22:重叠 I/O 模型

理解重叠 I/O 模型

第 21 章异步处理的并非 I/O,而是“通知”。本章讲解的才是以异步方式处理 I/O 的方法。

重叠 I/O

同一线程内部向多个目标传输数据引起的 I/O 重叠现象称为“重叠I/O”。为了完成这项任务,调用的 I/O 函数应立即返回,只有这样才能发送后续数据。从结果来看,利用上述模型收发数据时,最重要的前提条件就是异步 I/O(调用的 I/O 函数应以非阻塞模式工作)。

在这里插入图片描述

本章讨论的重叠 I/O 的重点不在于 I/O

重叠 I/O 的重点并非 I/O 本身,而是如何确认 I/O 完成时的状态。

非阻塞模式的输入输出需要另外确认执行结果。

Windows 平台下重叠 I/O 模型由非阻塞异步 I/O 函数和确认 I/O 完成状态的方法组成。

创建重叠 I/O 套接字

首先要创建适用于重叠I/O的套接字,可以通过如下函数完成:

#include <winsock2.h>SOCKET WSASocket(int af, int type, int protocol, LPWSAPROTOCOL_INFO loProtocolInfo,GROUP g,DWORD dwFlags
);

参数:

  • af:协议族信息
  • type:套接字数据传输方式
  • protocol:2 个套接字之间使用的协议信息
  • lpProtocolInfo:包含创建的套接字信息的WSAPROTOCOL_INFO结构体变量地址值,不需要时传递 NULL。
  • g:为扩展函数而预约的参数,可以使用 0
  • dwFlags:套接字属性信息

成功时返回套接字句柄,失败时返回 INVALID_SOCKET。

各位对前 3 个参数比较熟悉,第四个和第五个参数与目前的工作无关,可以简单设置为 NULL 和 0。可以向最后一个参数传递 WSA_FLAG_OVERLAPPED,赋予创建出的套接字重叠 I/O 特性。

可以通过如下函数调用创建出可以进行重叠 I/O 的非阻塞模式的套接字。

WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

执行重叠 I/O 的 WSASend 函数

创建出具有重叠 I/O 属性的套接字后,接下来 2 个套接字(服务器端/客户端之间的)连接过程与一般的套接字连接过程相同,但 I/O 数据时使用的函数不同。

先介绍重叠 I/O 中使用的数据输出函数:

#include <winsock2.h>int WSASend(SOCKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesSent,DWORD dwFlags,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

参数:

  • s:套接字句柄,传递具有重叠 I/O 属性的套接字句柄时,以重叠 I/O 模型输出。
  • IpBuffers:WSABUF 结构体变量数组的地址值,WSABUF 中存有待传输数据。
  • dwBufferCount:第二个参数中数组的长度。
  • IpNumberOfBytesSent:用于保存实际发送字节数的变量地址值
  • dwFlags:用于便改数据传输特性,如传递 MSG_OOB 时发送 OOB 模式的数据。
  • IpOverlapped:WSAOVERLAPPED 结构体变量的地址值,使用事件对象,用于确认完成数据传输。
  • IpCompletionRoutine:传入 Completion Routine 函数的入口地址值,可以通过该函数确认是否完成数据传输。

成功时返回 0,失败时返回 SOCKET_ERROR。

接下来介绍上述函数的第二个结构体参数类型,该结构体中存有待传输数据的地址和大小等信息。

typedef struct __WSABUF
{u_long len; // 待传输数据的大小char FAR * buf; // 缓冲地址值
} WSABUF, *LPWSABUF;

利用上述函数和结构体,传输数据时可以按如下方式编写代码:

WSAEVENT event;
WSAOVERLAPPED overlapped;
WSABUF dataBuf;
char buf[BUF_SIZE] = {"待传输的数据"};
int revcBytes = 0;
......
event = WSACreateEvent();
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = event;
dataBuf.len = sizeof(buf);
dataBuf.buf = buf;
WSASend(hSocket, &dataBuf, 1, &recvBytes, 0, &overlapped, NULL);
......

调用 WSASend 函数时将第三个参数设置为 1,因为策二个参数中待传输数据的缓冲个数为 1。另外,多余参数均设置为 NULL 或 0,其中需要注意第六个和第七个参数。

第六个参数中的 WSAOVERLAPPED 结构体定义如下:

typedef struct _WSAOVERLAPPED
{ DWORD Internal;DWORD InternalHigh;DWORD Offset;DWORD OffsetHigh;WSAEVENT hEvent;
} WSAOVERLAPPED, *LPWSAOVERLAPPED;

Internal、InternalHigh 成员是进行重叠 I/O 时操作系统内部使用的成员,而 Offset、OffsetHigh 同样属于具有特殊用途的成员。所以各位实际只需要关注 hEvent 成员。

关于 WSAOVERLAPPED 结构体有 3 点需要注意:

  1. 为了进行重叠 I/O,WSASend 函数的 lpOverlapped 参数中应该传递有效的结构体变量地址值,而不是 NULL。
  2. 若向 lpOverlapped 传递 NULL,WSASend 函数的第一个参数中的句柄所指的套接字将以阻
    塞模式工作。
  3. 利用 WSASend 函教同时向多个目标传输数据时,需要分别构建传入第六个参数的 WSAOVERLAPPED 结构体变量。这是因为,进行重叠 I/O 的过程中,操作系统将使用 WSAOVERLAPPED 结构体变量。

WSASend 函数调用过程中,函数返回时间点和数据传输完成时间点并非总不一致。分为以下两种情况:

  • 如果输出缓冲是空的,且传输的数据并不大,那么函数调用后可以立即完成数据传输。此时,WSASend 函数将返回 0,lpNumberOfBytesSent 中将保存实际传输的数据大小的信息。
  • 反之,WSASend 函数返回后仍需要传输数据时,将返回 SOCKET_ERROR,并将 WSA_IO_PENDING 注册为错误代码,该代码可以通过 WSAGetLastError 函数(稍后再介绍)得到。这时应该通过如下函效获取实际传输的数据大小。
#include <winsock2.h>BOOL WSAGetOverlappedResult(SOCKET s,LPWSAOVERLAPPED lpOverlapped,LPDWORD lpcbTransfer,BOOL fWait,LPDWORD lpdwFlags
);

参数:

  • s:进行重叠 I/O 的套接字句柄。
  • IpOverlapped:进行重叠 I/O 时传递的 WSAOVERLAPPED 结构体变量的地址值。
  • lpcbTransfer:用于保存实际传输的字节数的变量地址值。
  • fWait:如果调用该函数时仍在进行 I/O,fWait 为 TRUE 时等待 I/O 完成,fWait 为 FALSE 时将返回 FALSE 并跳出函数。
  • IpdwFlags:调用 WSARecv 函数时,用于获取附加信息(例如 OOB 消息)。如果不需要,可以传递 NULL。

成功时返回 TRUE,失败时返回 FALSE。

通过此函数不仅可以获取数据传输结果,还可以验证接收数据的状态。

进行重叠 I/O 的 WSARecv 函数

#include <winsock2.h>int WSARecv(SOCKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesRecvd,LPDWORD lpFlags,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

参数:

  • s:具有重叠 I/O 属性套接字句柄。
  • IpBuffers:用于保存接收数据的 WSABUF 结构体变量数组的地址值。
  • dwBufferCount:第二个参数中数组的长度。
  • lpNumberOfBytesRecvd:用于保存接收字节数的变量地址值。
  • lpFlags:用于设置或读取传输特性信息。
  • IpOverlapped:WSAOVERLAPPED 结构体变量地址值。
  • IpCompletionRoutine:Completion Routine 函数地址值。

成功时返回 0,失败时返回 SOCKET_ERROR。

Gather 输出指将多个缓冲中的数据累积到一定程度后一次性输出,Scatter 输入指将接收的数据分批保存。
重叠 I/O 的 WSASend 和 WSARecv 函数可以获得 writev & readv 函数的 Gather/Scatter I/O 功能。

重叠 I/O 的 I/O 完成确认

重叠 I/O 中有 2 种方法确认 I/O 的完成并获取结果。

  • 利用 WSASend、WSARecv 函数的第六个参数,基于事件对象。
  • 利用 WSASend、WSARecv 函数的第七个参数,基于 Completion Routine。

只有理解了这 2 种方法,才能算是掌握了重叠 I/O。首先介绍利用第六个参数的方法。

使用事件对象

直接给出示例。希望各位通过该示例验证如下 2 点:

  • 完成 I/O 时,WSAOVERLAPPED 结构体变量引用的事件对象将变为 signaled 状态。
  • 为了验证 I/O 的完成和完成结果,需要调用 WSAGetOvrlappedResult 函数。

发送端代码:

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>void ErrorHandling(char *msg);int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSocket;SOCKADDR_IN sendAdr;WSABUF dataBuf;char msg[] = "Network is Computer!";int sendBytes = 0;WSAEVENT evObj;WSAOVERLAPPED overlapped;if (argc != 3){printf("Usage : %s <IP> <port> \n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error");hSocket = WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);memset(&sendAdr, 0, sizeof(sendAdr));sendAdr.sin_family = AF_INET;sendAdr.sin_addr.s_addr = inet_addr(argv[1]);sendAdr.sin_port = htons(atoi(argv[2]));if (connect(hSocket, (SOCKADDR *)&sendAdr, sizeof(sendAdr)) == SOCKET_ERROR)ErrorHandling("connect() error");evObj = WSACreateEvent();memset(&overlapped, 0, sizeof(overlapped));overlapped.hEvent = evObj;dataBuf.len = strlen(msg) + 1;dataBuf.buf = msg;if (WSASend(hSocket, &dataBuf, 1, &sendBytes, 0, &overlapped, NULL) == SOCKET_ERROR){if (WSAGetLastError() == WSA_IO_PENDING){puts("Background data send");WSAWaitForMultipleEvents(1, &evObj, TRUE, WSA_INFINITE, FALSE);WSAGetOverlappedResult(hSocket, &overlapped, &sendBytes, FALSE, NULL);}else{ErrorHandling("WSASend() error");}}printf("Send data size: %d \n", sendBytes);WSACloseEvent(evObj);closesocket(hSocket);WSACleanup();return 0;
}void ErrorHandling(char *msg)
{fputs(msg, stderr);fputc('\n', stderr);exit(1);
}

上述示例调用的 WSAGetLastError 函数定义如下。调用套接字相关函数后,可以通过该函数获取错误信息。

#include<winsock2.h>int WSAGetLastError(void); // 返回错误代码(表示错误原因)

上述示例中该函数的返回值为 WSA_IO_PENDING,由此可以判断 WSASend 函数的调用结果并非发生了错误,而是尚未完成的状态。

下面介绍与上述示例配套使用的接收端代码:

在这里插入代码片

使用 Completion Routine 函数

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

搭建Redis哨兵集群

停掉现有的redis集群 因为这篇文章我是在 搭建完redis主从集群之后写的&#xff0c;如果要是没有搭建过这些&#xff0c;可以直接略过。要是从我上一篇 搭建redis主从集群过来的&#xff0c;可以执行下。 docker compose down 查找下redis相关进程 ps -ef | grep redis 可以看…

MySQL中,聚集索引和非聚集索引到底有什么区别?

文章目录 1. 数据存储方式2. 索引结构3. 查询效率4. 索引数量5. 适用场景6. 示例说明7. 总结 在MySQL中&#xff0c;聚集索引和非聚集索引&#xff08;也称二级索引&#xff09;的区别主要体现在数据存储方式、索引结构和查询效率等方面。以下是详细对比&#xff1a; 1. 数据存…

看 MySQL InnoDB 和 BoltDB 的事务实现

BoltDB 事务实现 BoltDB 支持多读单写方式的并发级别 事务操作会锁表 它的 MVCC 为 2 个版本&#xff0c;当前版本和正在写的版本 多读&#xff1a;可以并发读当前版本 单写&#xff08;串行写&#xff09;&#xff1a;写时拷贝当前 B 树&#xff0c;构建新 B 树&#xff…

08_JavaScript数据操作方法_数组

目录 一、创建一个数组 1.1 数组如何创建 字面量创建 构造函数创建 1.2 数组的长度 数组名.length 1.3 数组的索引 1.4 数组如何循环遍历 for 循环遍历 for in for of 二、数组的常用方法 &#xff08;重点 面试&#xff09; push 方法 unshift 方法 pop shif…

2025.3.25总结

工作&#xff1a;这两天工作都没啥产出&#xff0c;主要是工作状态不太好&#xff0c;周日晚上两点睡&#xff0c;周一晚上一点睡。熬夜伤身&#xff0c;但就是控制不住自己&#xff0c;睡前总要刷刷手机。本来想睡前看会书的&#xff0c;但这行为及其不稳定&#xff0c;抖音也…

《Python实战进阶》第33集:PyTorch 入门-动态计算图的优势

第33集&#xff1a;PyTorch 入门-动态计算图的优势 摘要 PyTorch 是一个灵活且强大的深度学习框架&#xff0c;其核心特性是动态计算图机制。本集将带您探索 PyTorch 的张量操作、自动求导系统以及动态计算图的特点与优势&#xff0c;并通过实战案例演示如何使用 PyTorch 实现…

初识哈希表

一、题意 给定一个整数数组 nums 和一个目标值 target&#xff0c;要求你在数组中找出和为目标值的那两个整数&#xff0c;并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素不能使用两遍。 示例&#xff1a; 给定 nums [2, 7, …

23种设计模式-创建型模式-单例

文章目录 简介问题1. 确保一个类只有一个实例2. 为该实例提供全局访问点 解决方案示例重构前&#xff1a;重构后&#xff1a; 拓展volatile 在单例模式中的双重作用 总结 简介 单例是一种创建型设计模式&#xff0c;它可以确保一个类只有一个实例&#xff0c;同时为该实例提供…

python裁剪nc文件数据

问题描述&#xff1a; 若干个nc文件储存全球的1850-2014年月尺度的mrro数据(或其他数据)&#xff0c;从1850-1到2014-12一共1980个月&#xff0c;要提取出最后35年1980.1~2014.12年也就是420个月的数据。 代码实现 def aaa(input_file,output_file,bianliang,start_index,en…

深入解析 Spring Framework 5.1.8.RELEASE 的源码目录结构

深入解析 Spring Framework 5.1.8.RELEASE 的源码目录结构 1. 引言 Spring Framework 是 Java 领域最流行的企业级开发框架之一&#xff0c;广泛用于 Web 开发、微服务架构、数据访问等场景。本文将深入解析 Spring Framework 5.1.8.RELEASE 的源码目录结构&#xff0c;帮助开…

数据清洗:基于python抽取jsonl文件数据字段

基于python抽取目录下所有“jsonl”格式文件。遍历文件内某个字段进行抽取并合并。 import os import json import time from tqdm import tqdm # 需要先安装&#xff1a;pip install tqdmdef process_files():# 设置目录路径dir_path r"D:\daku\关键词识别\1623-00000…

Windows 下使用 Docker 部署 Go 应用与 Nginx 详细教程

一、环境准备 1. 安装必要软件 Docker Desktop for Windows 下载地址&#xff1a;Docker Desktop: The #1 Containerization Tool for Developers | Docker 安装时勾选"使用 WSL 2 引擎"&#xff08;推荐&#xff09; WSL 2&#xff08;Windows Subsystem for Li…

C# .net ai Agent AI视觉应用 写代码 改作业 识别屏幕 标注等

C# net deepseek RAG AI开发 全流程 介绍_c# 向量处理 deepseek-CSDN博客 视觉多模态大模型 通义千问2.5-VL-72B AI大模型能看懂图 看懂了后能干啥呢 如看懂图 让Agent 写代码 &#xff0c;改作业&#xff0c;识别屏幕 标注等等。。。 据说是目前最好的免费图片识别框架 通…

Docker多阶段构建:告别臃肿镜像的终极方案

Docker多阶段构建:告别臃肿镜像的终极方案 你是否遇到过这样的问题:一个简单的应用,Docker镜像却高达1GB?编译工具、临时文件、开发依赖全被打包进去,导致镜像臃肿且不安全。 多阶段构建(Multi-stage Build) 就是为解决这一问题而生——它像搬家时“只带必需品”,让生…

大模型应用开发之大模型工作流程

一&#xff1a;大模型的问答工作流程 1.1: 分词和向量化 如上图所示&#xff0c;我们如果让大模型去回答问题&#xff0c;首先我们会输入一些文字给到大模型&#xff0c;大模型本质上是个数学模型&#xff0c;它是理解不了人类的整句话的&#xff0c;所以它会把我们的对应的句…

SpringMVC 请求处理

SpringMVC 请求处理深度解析&#xff1a;从原理到企业级应用实践 一、架构演进与核心组件协同 1.1 从传统Servlet到前端控制器模式 SpringMVC采用前端控制器架构模式&#xff0c;通过DispatcherServlet统一处理请求&#xff0c;相比传统Servlet的分散处理方式&#xff0c;实…

12届蓝桥杯—货物摆放

货物摆放 题目描述 小蓝有一个超大的仓库&#xff0c;可以摆放很多货物。 现在&#xff0c;小蓝有 nn 箱货物要摆放在仓库&#xff0c;每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向&#xff0c;每箱货物的边都必须严格平行于长、宽、高。 小蓝希望所…

Reactor/Epoll为什么可以高性能?

在 Reactor 模式中使用 epoll_wait 实现低 CPU 占用率的核心原理是 ​事件驱动的阻塞等待机制&#xff0c;而非忙等待。以下通过分步骤解析其工作原理和性能优势&#xff1a; void network_thread() {int epoll_fd epoll_create1(0);epoll_event events[MAX_EVENTS];// 添加U…

批量优化与压缩 PPT,减少 PPT 文件的大小

我们经常能够看到有些 PPT 文档明明没有多少内容&#xff0c;但是却占用了很大的空间&#xff0c;存储和传输非常的不方便&#xff0c;这时候通常是因为我们插入了一些图片/字体等资源文件&#xff0c;这些都可能会导致我们的 PPT 文档变得非常的庞大&#xff0c;今天就给大家介…

Java基础 3.22

1.break练习 //1-100之内的数求和&#xff0c;求当和第一次大于20的当前数i public class Break01 {public static void main(String[] args) {int n 0;int count 0;for (int i 1; i < 100; i) {count i;System.out.println("当前和为" count);if (count &g…