【第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.mzph.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;其流程管理体系的构建与运营是其成功的关键之一。本文将从华为流程管理体系的核心理念、构建…