其实关于这个模型,网络上也有一个案例说明

老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了,牙也不疼了,微软提供的WSAAsyncSelect模型就是这个意思。

异步选择(WSAAsyncSelect)模型是一个有用的异步 I/O 模型。利用这个模型,应用程序可在一个套接字上,接收以 Windows 消息为基础的网络事件通知。具体的做法是在建好一个套接字后,调用WSAAsyncSelect函数。该模型的核心即是WSAAsyncSelect函数。

WSAAsyncSelect函数定义如下:

c++

int WSAAPI WSAAsyncSelect(
SOCKET s,
HWND hWnd,
u_int wMsg,
long lEvent
);

delphi

function WSAAsyncSelect(s: TSocket; hWnd: HWND; wMsg: u_int; lEvent: Longint): Integer; stdcall;

参数说明

参数名 具体含义
s 指定的是我们感兴趣的那个套接字。
hwnd 指定一个窗口句柄,它对应于网络事件发生之后,想要收到通知消息的那个窗口。
wMsg 指定在发生网络事件时,打算接收的消息。该消息会投递到由hWnd窗口句柄指定的那个窗口。
lEvent 指定一个位掩码,对应于一系列网络事件的组合

注意事项

  • wMsg参数指定的消息通常是我们自定义的消息,应用程序需要将这个消息设为比Windows的WM_USER大的一个值,避免网络窗口消息与系统预定义的标准窗口消息发生混淆与冲突。

  • lEvent参数指定的网络类型为:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT、FD_CLOSE等。当然,到底使用FD_ACCEPT,还是使用FD_CONNECT类型,要取决于应用程序的身份是客户端,还是服务器。如应用程序同时对多个网络事件有兴趣,只需对各种类型执行一次简单的按位OR(或)运算就OK。

Value Meaning
FD_READ 应用程序想要接收有关是否可读的通知,以便读入数据
FD_WRITE 应用程序想要接收有关是否可写的通知,以便写入数据
FD_ACCEPT 应用程序想接收与进入连接有关的通知
FD_CONNECT 应用程序想接收与一次连接完成的通知
FD_CLOSE 应用程序想接收与套接字关闭的通知

摘取MSDN说明的部分字段,完整说明参阅:https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaasyncselect

  • 多个事件务必在套接字上一次注册!另外还要注意的是,一旦在某个套接字上允许了事件通知,那么以后除非明确调用closesocket命令,或者由应用程序针对那个套接字调用了WSAAsyncSelect,从而更改了注册的网络事件类型,否则的话,事件通知会永远有效!若将lEvent参数设为0,效果相当于停止在套接字上进行的所有网络事件通知

  • 若应用程序针对一个套接字调用了WSAAsyncSelect,那么套接字的模式会从“阻塞”变成“非阻塞”。这样以来,如果调用了像WSARecv这样的Winsock函数,但当时却并没有数据可用,那么必然会造成调用的失败,并返回WSAEWOULDBLOCK错误。为防止这一点,应用程序应依赖于由WSAAsyncSelect的uMsg参数指定的用户自定义窗口消息,来判断网络事件类型何时在套接字上发生;而不应盲目地进行调用。

应用程序在一个套接字上成功调用了WSAAsyncSelect之后,会在与hWnd窗口句柄对应的窗口例程中,以Windows消息的形式,接收网络事件通知。窗口例程通常定义如下:

LRESULT CALLBACK WindowProc(
HWND hwnd, //指定一个窗口的句柄,对窗口例程的调用正是由那个窗口发出的。
UINT uMsg, //指定需要对哪些消息进行处理。这里我们感兴趣的是WSAAsyncSelect调用中定义的消息。
WPARAM wParam, //指定在其上面发生了一个网络事件的套接字。(假若同时为这个窗口例程分配了多个套接字,这个参数的重要性便显示出来了。)
LPARAM lParam //包含了两方面重要的信息。其中, lParam的低字(低位字)指定了已经发生的网络事件,而lParam的高字(高位字)包含了可能出现的任何错误代码。
);
  • Delphi对这个函数的参数做了封装,对应的结构体是TMessage,所以我们实际使用的只需要定义对应消息的处理函数即可

  • 大家可以看出上面的文字说明很明显是C++的,大部分内容我是摘抄自网络,Delphi版的我在网上没找到啥有用资料

参考博客:https://www.cnblogs.com/venow/archive/2012/06/09/2543053.html

代码实现

unit MainFrm;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls; const
MY_WM_SOCKET = WM_USER + 55; type
TForm1 = class(TForm)
Button1: TButton;
StatusBar1: TStatusBar;
Memo1: TMemo;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure WMSocket(var Msg: TMessage); message MY_WM_SOCKET;
public
{ Public declarations }
end; var
Form1: TForm1; implementation {$R *.dfm} uses Winapi.WinSock2, ScktComp; var
WSAData: TWSAData;
// 套接字对象,用于监听
ClientSocket, Server: TSocket;
ServerRecord: sockaddr_in; procedure TForm1.Button1Click(Sender: TObject);
begin
// 初始化版本库
if WSAStartup(WINSOCK_VERSION, WSAData) <> ERROR_SUCCESS then
begin WSACleanup;
Self.StatusBar1.Panels[0].Text := '初始化失败';
Exit;
end; // 初始化socket
Server := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
// 创建失败
if Server = INVALID_SOCKET then
begin
closesocket(Server);
WSACleanup;
Self.StatusBar1.Panels[0].Text := '初始化socket失败';
Exit;
end; // 指定IP、端口号和协议类型
with ServerRecord do
begin sin_family := PF_INET;
sin_port := htons(10086);
sin_addr.S_addr := inet_addr(PAnsiChar(AnsiString('127.0.0.1')));;
end; // 绑定IP和端口号 if bind(Server, TSockAddr(ServerRecord), SizeOf(ServerRecord)) = SOCKET_ERROR
then
begin
closesocket(Server);
WSACleanup;
Self.StatusBar1.Panels[0].Text := '端口号被占用';
Exit;
end; if listen(Server, SOMAXCONN) = SOCKET_ERROR then
begin
closesocket(Server);
WSACleanup;
Self.StatusBar1.Panels[0].Text := '监听失败';
Exit;
end;
// 核心函数
WSAAsyncSelect(Server, Self.Handle, MY_WM_SOCKET, FD_ACCEPT or FD_READ or
FD_WRITE or FD_CLOSE);
// 禁用按钮
Button1.Enabled := false; end; procedure TForm1.FormCreate(Sender: TObject);
begin
if WSACleanup <> ERROR_SUCCESS then Self.StatusBar1.Panels[0].Text := '初始化失败'; if Server <> INVALID_SOCKET then closesocket(Server);
Self.StatusBar1.Panels[0].Text := '网络库初始化成功';
end; // 当产生网络消息的时候核心的处理函数
procedure TForm1.WMSocket(var Msg: TMessage);
begin
if (Msg.Msg = MY_WM_SOCKET) then
Self.StatusBar1.Panels[0].Text := '网络消息'; case WSAGetSelectEvent(Msg.LParam) of
FD_ACCEPT:
begin
var
AddSize := SizeOf(ServerRecord); ClientSocket := accept(Server, @ServerRecord, @AddSize);
var
CustomWinSocket := TCustomWinSocket.Create(ClientSocket);
Form1.Memo1.Lines.Add('客户端IP:' + CustomWinSocket.RemoteAddress);
end;
FD_READ:
begin end;
FD_WRITE:
begin end;
end;
end; end.

客户端代码不变,可以使用相同模型也可以不同模型

网络IO模型-异步选择模型(Delphi版)的更多相关文章

  1. Winsock—I/O模型之选择模型(一)

    Winsock中提供了一些I/O模型帮助应用程序以异步方式在一个或多个套接字上管理I/O. 这样的I/O模型有六种:阻塞(blocking)模型,选择(select)模型,WSAAsyncSelect ...

  2. delphi异步选择模型编程TCP

    Server端: unit U_FrmServer; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, ...

  3. DELPHI异步选择模型UDP

    unit U_FrmServer; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Di ...

  4. 二.Windows I/O模型之异步选择(WSAAsyncSelect)模型

    1.基于windows消息为基础的网络事件io模型.因此我们必须要在窗口程序中使用该模型.该模型中的核心是调用WSAAsyncSelect函数实现异步I/O. 2.WSAAsyncSelect函数:注 ...

  5. C#异步编程模型

    什么是异步编程模型 异步编程模型(Asynchronous Programming Model,简称APM)是C#1.1支持的一种实现异步操作的编程模型,虽然已经比较“古老”了,但是依然可以学习一下的 ...

  6. 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出 ...

  7. 转 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    此文章为转载,如有侵权,请联系本人.转载出处,http://blog.chinaunix.net/uid-28458801-id-4464639.html 同步(synchronous) IO和异步( ...

  8. windows下的IO模型之异步选择(WSAAsyncSelect)模型

    异步选择(WSAAsyncSelect)模型是一个有用的异步I/O 模型.其核心函数是WSAAsyncSelect,该函数是非阻塞的 (关于异步io的理解详情可以看:http://www.cnblog ...

  9. Java网络编程和NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型

    Java网络编程与NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型 知识点 nio 下 I/O 阻塞与非阻塞实现 SocketChannel 介绍 I/O 多路复用的原理 事件选择器与 ...

随机推荐

  1. 深入理解 JVM 的内存区域

    深入理解运行时数据区 代码示例: 1. JVM 向操作系统申请内存: JVM 第一步就是通过配置参数或者默认配置参数向操作系统申请内存空间,根据内存大小找到具体的内存分配表,然后把内存段的起始地址和终 ...

  2. h5内嵌微信小程序,调用微信支付功能

    在小程序中不能使用之前在浏览器中配置的支付功能,只能调用小程序专属的api进行支付. 因为需要在现在实现的基础上,再添加在小程序中调用微信支付功能,所以我的思路是这样的 1.在点击支付按钮时,判断是不 ...

  3. 面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 算法是数据结构的灵魂! 好的算法搭配上合适的数据结构,可以让代码功能大大的提升效率. ...

  4. jenkins打包java项目缺少jar包问题解决

    java项目在使用jenkins打包时个别jar包可能会没有下载到本地,这时候就要用命令行本地安装一下 打包时基础jar包报错如图: 黑框里依次为: 组ID:-DgroupId=com.azazar ...

  5. jackson序列化与反序列化的应用实践

    jackson序列化与反序列化的应用实践 源码地址: https://github.com/zhouweixin/serializable 1 相关概念 序列化: 把对象转换为字节序列的过程称为对象的 ...

  6. Web开发初探(系统理解Web知识点)

    一.Web开发介绍 我们看到的网页通过代码来实现的 ,这些代码由浏览器解释并渲染成你看到的丰富多彩的页面效果. 这个浏览器就相当于Python的解释器,专门负责解释和执行(渲染)网页代码. 写网页的代 ...

  7. oracle之三手工备份与恢复

    手工备份与恢复 2.1 手工备份和恢复的命令 1)备份和还原都使用OS命令,如linux中的cp 2)恢复用sqlplus命令:recover 2.2 备份前要对数据库进行检查: 1) 检查需要备份的 ...

  8. 吴恩达《深度学习》-课后测验-第二门课 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)-Week 1 - Practical aspects of deep learning(第一周测验 - 深度学习的实践)

    Week 1 Quiz - Practical aspects of deep learning(第一周测验 - 深度学习的实践) \1. If you have 10,000,000 example ...

  9. [LeetCode]3. 无重复字符的最长子串(滑动窗口)

    题目 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc ...

  10. [算法题]最小m划分(划分DP)

    题目描述 给定 n 个整数组成的序列,将序列分割为 m 段,如何分割才能使这 m 段子序列的和的最大值达到最小? 题解 状态表示 dp[i][j]表示前i个元素划分j段 子序列和的最大值的最小值 状态 ...