《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.pswp.cn/diannao/75538.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/75538.shtml
英文地址,請注明出處:http://en.pswp.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…