【第22節】windows網絡編程模型(WSAAsyncSelect模型)

目錄

引言

一、WSAAsyncSelect模型概述

二、WSAAsyncSelect模型流程

2.1 自定義消息

2.2 創建窗口例程

2.3 初始化套接字

2.4 注冊網絡事件

2.5 綁定和監聽

2.6 消息循環

三、完整示例代碼


引言

????????在網絡編程的廣袤天地中,高效處理網絡事件是構建穩定應用的關鍵。WSAAsyncSelect模型作為一種獨特且實用的網絡編程模型,為開發者提供了異步處理網絡事件的有力手段。它巧妙地將Windows窗口消息機制與套接字相結合,讓應用程序能夠基于消息通知,及時響應各類網絡事件。接下來,讓我們深入探究WSAAsyncSelect模型的工作原理、具體流程以及在實際編程中的應用,一同解鎖其在網絡編程領域的強大潛力。

一、WSAAsyncSelect模型概述

????????Windows 套接字異步選擇模型,要是想在應用程序里用上WSAAsyncSelect模型,第一步就是用CreateWindow函數創建一個窗口,緊接著得給這個窗口配備一個窗口回調函數(WinProc)。除了創建窗口,使用對話框也是可行的,這種情況下就得給對話框配上對話框回調函數。

????????WinSock給出了一個特別好用的異步I/O模型。依靠這個模型,應用程序可以在某個套接字上,接收那些基于Windows消息的網絡事件通知。

????????WSAAsyncSelect模型的實現辦法是這樣的:調用WSASyncSelect函數,這么做會自動把套接字切換到非阻塞模式,與此同時,還能注冊一個或者多個你關心的網絡事件。它會把套接字、窗口句柄以及自定義消息捆綁到一塊兒。只要之前注冊的網絡事件發生了,對應的窗口就會收到一個基于消息的通知 。

二、WSAAsyncSelect模型流程

2.1 自定義消息

????????用戶需要自定義一個消息。當相關網絡事件消息出現時,這個自定義消息會被發送到消息隊列中。一般有以下兩種自定義消息的方式:
1. 靜態注冊消息:

#define WM_MYSOCKETMSG WM_USER + 100; //具體查看自定義消息的范圍

2. 動態注冊消息:

#define MYWN_SOCKET L"MYWN_SOCK" //自定義一個字符串
UINT g_nNetMsgID = RegisterWindowMessage(MYWN_SOCKET);

2.2 創建窗口例程

????????利用WSAAsyncSelect()函數開發WinSock應用程序,離不開Windows窗口。在窗口實例中接收用戶自定義的消息。以Win32應用程序為例:

HWND g_SockHwnd = NULL; //接收SOCKET消息的窗口句柄
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {HWND hWnd;hInst = hInstance; //將實例句柄存儲在全局變量中hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);g_SockHwnd = hWnd;if (!hWnd)return FALSE;ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return TRUE;
}

2.3 初始化套接字

????????初始化組件和創建套接字的方法之前已經介紹過。

#include"winsock2.h"
#pragma comment(lib,"WS2_32.lib")
WSADATA stcData;
int nResult = 0;
nResult = WSAStartup(MAKEWORD(2, 2), &stcData);
//2.創建套接字
SOCKET sSocket = socket(AF_INET, SOCK_STREAM, 0);

2.4 注冊網絡事件

????????WSAAsyncSelect函數有兩個主要作用,一是它能自動把套接字設置成非阻塞模式,二是會給套接字關聯上一個窗口句柄。一旦有網絡事件出現,比如連接建立、數據發送或接收等情況發生,WSAAsyncSelect函數就會把相關信息發送到之前綁定的那個窗口。這樣,應用程序在接收到像是連接、發送、接收這類網絡通知時,對應的具體信息就會被投放到窗口消息隊列當中 。

int WSAAsyncSelect(SOCKET s,         //套接字句柄HWND hWnd,        //要響應事件的窗口句柄unsigned int wMsg, //自定義的消息long lEvent       //注冊的網絡事件
);

?????????注:其中窗口句柄是在創建主窗口時獲得的。注冊網絡事件通常在創建時設定連接通知和關閉通知。
(1)FD_READ事件觸發條件:
??? - 在數據到達socket后,并且前一個recv()調用完畢。
??? - 調用recv()后,緩沖區還有未讀完的數據時,還會繼續響應該事件。
(2)FD_WRITE事件觸發條件:
??? - 第一次connect()或accept()后(即連接建立后)。
??? - 調用send()返回WSAEWOULDBLOCK錯誤后,再次調用send()或sendto函數成功時。
(3)FD_ACCEPT事件觸發條件:當有請求建立連接,并且前一個accept()調用后。
(4)FD_CLOSE事件觸發條件:自己或客戶端中斷連接后。
(5)FD_CONNECT事件觸發條件:調用了connect(),并且連接建立后。
示例:

WSAAsyncSelect(sSocket,g_SockHwnd,  g_nNetMsgID,    //當前服務端的SOCK句柄//當前服務端的窗口句柄//當有網絡事件響應時窗口接收的消息FD_ACCEPT | FD_CLOSE);//需要響應的網絡事件消息

2.5 綁定和監聽

1. 初始化地址定址:

sockaddr_in sAddr = {0};
sAddr.sin_family = AF_INET;
sAddr.sin_port = htons(1234);
sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

2. 綁定:

int nRet = 0;
nRet = bind(sSocket,(sockaddr*)&sAddr,sizeof(sockaddr_in));
//接收返回信息
//當前客戶端SOCK句柄
//IP定址
//IP定址結構體大小

3. 監聽:

nRet = listen(sSocket, SOMAXCONN);
//當前服務端的SOCK句柄
//等待連接的最大隊列長度

2.6 消息循環

在消息循環中實現自定義消息的處理過程。

WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

當網絡事件消息抵達一個窗口回調函數后:
- `message`為當前自定義消息。
- `wParam`為當前響應網絡事件的套接字。
- `lParam`的高16位為錯誤碼,低16位為具體的網絡事件。
通過以下宏來獲取相關信息:
- `WSAGETSELECTERROR`:返回`lParam`高字位包含的錯誤信息。
- `WSAGETSELECTEVENT`:根據得到的`lParam`的低字部分確定具體是哪一類網絡事件。


示例代碼:

int lError = WSAGETSELECTERROR(lParam); //高16位表示錯誤碼
int lEvent = WSAGETSELECTEVENT(lParam);  //低字節為發生的網絡事件
SOCKET MsgSocket = (SOCKET)wParam;       //消息事件
switch (lEvent) {
case FD_ACCEPT:sockaddr_in ClientAddr = {};int nClientLength = sizeof(ClientAddr);SOCKET sClientSock = accept(MsgSocket,//客戶端地址信息//客戶端地址信息長度//當前服務端的SOCK句柄(sockaddr*)&ClientAddr, &nClientLength);//設置消息模式WSAAsyncSelect(sClientSock, g_SockHwnd, g_nNetMsgID,FD_READ | FD_WRITE | FD_CLOSE);
}

接收不到網絡事件的原因
????????1. 在同一個套接字上,自定義的網絡事件窗口消息被多次調用WSAAsyncSelect()函數注冊不同的網絡事件,這種情況下以最后一次注冊的網絡事件為準。例如:

WSAAsyncSelect(s, hWnd, wm_msg, FD_READ);
WSAAsyncSelect(s, hWnd, wm_msg, FD_ACCEPT);

????????2. 在同一個套接字上多次調用WSAAsyncSelect()函數,且使用了不同的網絡事件窗口消息。例如:

WSAAsyncSelect(s, hWnd, wm_msg1, FD_READ);
WSAAsyncSelect(s, hWnd, wm_msg2, FD_READ);

以下是示例代碼部分:

#include "WSAAsyncSelect.h"
UINT g_nNetMsgID = 0;
HWND g_SockHwnd = NULL;
SOCKET g_sClientSock = NULL;
//套接字消息
//接收SOCKET消息的窗口句柄
//客戶端Socket句柄
#define WM_MYSOCKETMSG WM_USER + 100;
BOOL AsyncSelectTCP() {//1.初始化套接字WSADATA stcData;int nResult = 0;nResult = WSAStartup(MAKEWORD(2, 2), &stcData);if (nResult == SOCKET_ERROR)return FALSE;g_nNetMsgID = RegisterWindowMessage(MYWN_SOCKET);//2.創建套接字SOCKET sSocket = Socket(AF_INET, SOCK_STREAM, 0);//3.注冊感興趣的網絡事件//注冊消息//當前服務端的SOCK句柄int nRet = WSAAsyncSelect(sSocket, g_SockHwnd,//當前服務端的窗口句柄g_nNetMsgID,//當有網絡事件響應時窗口接收的消息FD_ACCEPT | FD_CLOSE);//需要響應的網絡事件消息if (nRet) {MessageBox(NULL, L"", L"在監聽SOCKET上設置網絡消息失敗", MB_OK);goto CloseSock;}//4.初始化地址定址sockaddr_in sAddr = {0};sAddr.sin_family = AF_INET;sAddr.sin_port = htons(1234);sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//5.綁定nRet = bind(sSocket,(sockaddr*)&sAddr,sizeof(sockaddr_in));//當前客戶端SOCK句柄//IP定址//IP定址結構體大小if (SOCKET_ERROR == nRet) {MessageBox(NULL, L"", L"綁定到指定地址端口出錯!", MB_OK);goto CloseSock;}//6.監聽  在調用WSAAsyncSelect后sSocket已經是非阻塞模式nRet = listen(sSocket,            //當前服務端的SOCK句柄SOMAXCONN);          //等待連接的最大隊列長度if (SOCKET_ERROR == nRet) {MessageBox(NULL, L"錯誤", L"SOCKET進入監聽模式出錯!", MB_OK);goto CloseSock;}return TRUE;
CloseSock:closesocket(sSocket);WSACleanup();return FALSE;
}
//************************************
//函數名稱: SocketMsg響應網絡事件消息
//返回值:
//參數:
//參數:
//參數:
//參數:
//************************************
void SocketMsg(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {int iError = WSAGETSELECTERROR(lParam);int lEvent = WSAGETSELECTEVENT(lParam);SOCKET MsgSocket = (SOCKET)wParam;switch (lEvent) {//高16位表示錯誤碼//低字節為發生的網絡事件//響應消息事件的套接字case FD_ACCEPT: {sockaddr_in ClientAddr = {};int nClientLength = sizeof(ClientAddr);//客戶端地址信息長度if (!g_sClientSock)   //當前例子只允許連接一個客戶端{g_sClientSock = accept(MsgSocket,   //當前服務端的SOCK句柄(sockaddr*)&ClientAddr,&nClientLength);//重新為該消息設置網絡事件WSAAsyncSelect(sClientSock, g_SockHwnd, g_nNetMsgID,FD_READ | FD_WRITE | FD_CLOSE);}break;}case FD_CLOSE: {closesocket(g_sClientSock);}break;case FD_READ:char szBufTmp[1024] = {0};if (g_sClientSock) {int iRecv = recv(MsgSocket, szBufTmp, 1024, 0);if (SOCKET_ERROR == iRecv || 0 == iRecv) {if (WSAEWOULDBLOCK == WSAGetLastError())Sleep(20);//停20ms}else {//顯示信息}}break;}
}

在`XXX主窗口.cpp`文件中:
????????1. 在`InitInstance`函數中創建窗口時,保存該窗口句柄:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {HWND hWnd;hInst = hInstance; //將實例句柄存儲在全局變量中hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);g_SockHwnd = hWnd;if (!hWnd) {return FALSE;}ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return TRUE;
}


????????2. 在窗口回調函數中判斷當前消息是否為自定義消息:

if (g_nNetMsgID == message) {SocketMsg(hWnd, message, wParam, lParam);
}

????????WSAAsyncSelect模型通過將套接字與窗口消息機制相結合,為網絡編程提供了一種異步處理網絡事件的方式,在實際應用中有助于提升程序對網絡事件的響應效率和處理能力 。

?

三、完整示例代碼

TCP客戶端代碼:

待補充

- 這是一個基礎的TCP客戶端實現
- 主要功能:
? - 連接到服務器(127.0.0.1:0x1234)
? - 使用多線程處理接收消息
? - 通過控制臺輸入發送消息
? - 基本的錯誤處理?

WSAAsyncSelect模型服務端:

待補充

?這是一個基于Windows消息機制的TCP服務器實現
- 主要特點:
? - 使用WSAAsyncSelect實現異步通信
? - 通過Windows消息機制處理網絡事件
? - 支持多客戶端連接
? - 使用自定義消息(WM_MYSOCKET)處理網絡事件
? - 在界面上顯示連接狀態和消息

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/73060.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/73060.shtml
英文地址,請注明出處:http://en.pswp.cn/web/73060.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

利用Dify編制用戶問題意圖識別和規范化回復

繼上一篇文章,成功完成Dify本地部署后,主要做了一些workflow和Agent的應用實現,整體感覺dify在工作流可視化編排方面非常好,即使部分功能無法實現,也可以通過代碼執行模塊或者自定義工具來實現(后續再具體分…

雙核鎖步技術在汽車芯片軟錯誤防護中的應用詳解

摘要 本文深入探討了雙核鎖步技術在保障汽車芯片安全性中的應用。文章首先分析了國產車規芯片在高安全可靠領域面臨的軟錯誤難點及攻克方向,然后詳細介紹了雙核鎖步技術的基本原理及其在汽車芯片防軟錯誤的重要性。通過對比國內外多家廠商的芯片技術,分析…

Lustre 語言的 Rust 生成相關的工作

目前 Lustre V6 編譯器支持編譯生成的語言為C語言。但也注意到,以 Rust 語言為生成目標語言,也存在若干相關工作。 rustre(elegaanz) 該項工作為 Lustre v6 語言的解析器,使用 Rust 語言實現。生成 Lustre AST。 項…

Java 之「單調棧」:從入門到實戰

Java 單調棧:從入門到實戰 文章目錄 Java 單調棧:從入門到實戰引言什么是單調棧?單調遞增棧單調遞減棧 單調棧的應用場景Java 實現單調棧代碼示例:下一個更大元素代碼解析 單調棧的優勢實戰應用:股票價格跨度代碼示例代…

【Golang】defer與recover的組合使用

在Go語言中,defer和recover是兩個關鍵特性,通常結合使用以處理資源管理和異常恢復。以下是它們的核心應用場景及使用示例: 1. defer 的應用場景 defer用于延遲執行函數調用,確保在函數退出前執行特定操作。主要用途包括&#xff…

CSS 中flex - grow、flex - shrink和flex - basis屬性的含義及它們在彈性盒布局中的協同作用。

大白話CSS 中flex - grow、flex - shrink和flex - basis屬性的含義及它們在彈性盒布局中的協同作用。 在 CSS 的彈性盒布局(Flexbox)里,flex-grow、flex-shrink 和 flex-basis 這三個屬性對彈性元素的尺寸和伸縮性起著關鍵作用。下面為你詳細…

OpenGL ES ->乒乓緩沖,計算只用兩個幀緩沖對象(Frame Buffer Object)+疊加多個濾鏡作用后的Bitmap

乒乓緩沖核心思想 不使用乒乓緩沖,如果要每個濾鏡作用下的繪制內容,也就是這個濾鏡作用下的幀緩沖,需要創建一個Frame Buffer Object加上對應的Frame Buffer Object Texture使用乒乓緩沖,只用兩個Frame Buffer Object加上對應的F…

【HarmonyOS NEXT】關鍵資產存儲開發案例

在 iOS 開發中 Keychain 是一個非常安全的存儲系統,用于保存敏感信息,如密碼、證書、密鑰等。與文件系統不同,Keychain 提供了更高的安全性,因為它對數據進行了加密,并且只有經過授權的應用程序才能訪問存儲的數據。那…

ccfcsp1901線性分類器

//線性分類器 #include<iostream> using namespace std; int main(){int n,m;cin>>n>>m;int x[1000],y[1000];char z[1000];for(int i0;i<n;i){cin>>x[i]>>y[i];cin>>z[i];}int a[20],b[20],c[20];for(int i0;i<m;i){cin>>a[i…

Spring Boot 整合 OpenFeign 教程

精心整理了最新的面試資料和簡歷模板&#xff0c;有需要的可以自行獲取 點擊前往百度網盤獲取 點擊前往夸克網盤獲取 Spring Boot 整合 OpenFeign 教程 一、OpenFeign 簡介 OpenFeign 是 Netflix 開源的聲明式 HTTP 客戶端&#xff0c;通過接口和注解簡化服務間 HTTP 調用。…

APM 仿真遙控指南

地面站開發了一段時間了&#xff0c;由于沒有硬件&#xff0c;所以一直在 APM 模擬器中驗證。我們已經實現了 MAVLink 消息接收和解析&#xff0c;顯示無人機狀態&#xff0c;給無人機發送消息&#xff0c;實現一鍵起飛&#xff0c;飛往指定地點&#xff0c;降落&#xff0c;返…

C語言入門教程100講(4)輸入輸出

文章目錄 1. 什么是輸入輸出&#xff1f;2. 標準輸入輸出函數2.1 printf 函數2.2 scanf 函數 3. 格式化占位符4. 示例代碼代碼解析&#xff1a;輸出結果&#xff1a; 5. 常見問題問題 1&#xff1a;scanf 中的 & 是什么作用&#xff1f;問題 2&#xff1a;printf 和 scanf …

《信息系統安全》(第一次上機實驗報告)

實驗一 &#xff1a;網絡協議分析工具Wireshark 一 實驗目的 學習使用網絡協議分析工具Wireshark的方法&#xff0c;并用它來分析一些協議。 二實驗原理 TCP/IP協議族中網絡層、傳輸層、應用層相關重要協議原理。網絡協議分析工具Wireshark的工作原理和基本使用規則。 三 實…

城市街拍人像自拍電影風格Lr調色教程,手機濾鏡PS+Lightroom預設下載!

調色教程 城市街拍人像自拍的電影風格 Lr 調色&#xff0c;是利用 Adobe Lightroom 軟件&#xff0c;對在城市街景中拍攝的人像自拍照片進行后期處理&#xff0c;使其呈現出電影畫面般獨特的視覺質感與藝術氛圍。通過一系列調色操作&#xff0c;改變照片的色彩、明暗、對比等元…

自學Python創建強大AI:從入門到實現DeepSeek級別的AI

人工智能&#xff08;AI&#xff09;是當今科技領域最熱門的方向之一&#xff0c;而Python是AI開發的首選語言。無論是機器學習、深度學習還是自然語言處理&#xff0c;Python都提供了豐富的庫和工具。如果你夢想創建一個像DeepSeek這樣強大的AI系統&#xff0c;本文將為你提供…

Qt/C++項目積累:4.遠程升級工具 - 4.1 項目設想

背景&#xff1a; 桌面程序一般都支持遠程升級&#xff0c;也是比較常用的場景設計。如酷狗音樂的升級&#xff0c;會提供兩個選項&#xff0c;自動幫助安裝或是新版本提醒&#xff0c;由用戶來決定是否升級&#xff0c;都屬于遠程升級的應用及策略。 看看經過這塊的功能了解及…

(一)丶Windows安裝RabbitMQ可能會遇到的問題

一丶可能會忘了配置ERLang的環境變量 二丶執行命令時報錯 第一步 rabbitmq-plugins enable rabbitmq_management 第二部 rabbitmqctl status 三丶修改.erlang.cookie 文件 1.找到C盤目下的.erlang.cookie文件 C:\Users\admin\.erlang.cookie C:\Windows\System32\config\sys…

Amdahl 定律

Amdahl 定律是用來表示&#xff0c;當提高系統某部分性能時對整個系統的影響&#xff0c;其公式如下&#xff1a; a表示我們提升部分初始耗時比例&#xff0c;k是我們的提升倍率&#xff0c;通過這個公式我們可以輕松的得知對每一部分的提醒&#xff0c;對整個系統帶來的影響…

HW華為流程管理體系精髓提煉華為流程運營體系(124頁PPT)(文末有下載方式)

資料解讀&#xff1a;HW華為流程管理體系精髓提煉華為流程運營體系&#xff08;124頁PPT&#xff09; 詳細資料請看本解讀文章的最后內容。 華為作為全球領先的科技公司&#xff0c;其流程管理體系的構建與運營是其成功的關鍵之一。本文將從華為流程管理體系的核心理念、構建…