在MFC中能否使用异步IO的事件选择模型
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在MFC中能否使用异步IO的事件选择模型相关的知识,希望对你有一定的参考价值。
我想用事件选择模型来编写一个带有图形界面的网络编程程序
可是在MFC中试了好几个方法也没成功 请问是什么原因 是不是有别的方法
请高手解答
你要不要参考一下我写的这个程序,文件名是写死在程序里的……
从c:\1.dat发到c:\2.dat
代码的话看最下面的最新追答里的。追问
对不起 我不知道最新追答在哪 不过我看了你的空间 知道你是个牛人 你能发个完整的工程给我吗 先谢谢你了
追答……就是那个问题有一个答案是我回答的,下面有很多追问再回答追问再回答,最后一个。
我这里也传一份好了
不保证代码里是不是还有什么比较带坏头的bug之类的
就是最上面的那个网址的内容吧 我正在看呢 打算一会自己写一个 我几个地方不明白 分别是 attach(nsock.detach())和 AsyncSelect() 这两个都是干什么用的 我以前没见过
追答detach是把里面实质有用的内容和对象分离,这样在对象析构的时候就不会把里面的内容给释放掉。
attch相反,把里面实质的内容加载到自己的对象里。
你应该有听说过对象是对其他一些什么什么的封装这种说法吧。
另外AsyncSelect就是事件选择啊……你在问题的标题不是说了事件选择模型吗
刚百度查了attach()和detach() 我觉得你的解释容易懂 AsyncSelect是不是跟WSAAsyncSelect类似 你的回答帮我很多 谢谢你
追答AsyncSelect就是WSAAsyncSelect。
两个是同一个东西。
前者是MFC里的面向对象的东西,后者是Windows API里的直接调用的东西。
前者封装了后者。
再怎么用AsyncSelect,到了最后都是调用WSAAsyncSelect的
CAsynSocket类可以使用事件选择模型吗 你以前试过吗
追答可以用 不过没有win32好用 需要派生一个casyncsocket子类进行事件选择
追问你有没有以前做过的程序 发一个给我可以吗
网络IO模型-异步选择
其实关于这个模型,网络上也有一个案例说明
异步选择(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.
客户端代码不变,可以使用相同模型也可以不同模型
以上是关于在MFC中能否使用异步IO的事件选择模型的主要内容,如果未能解决你的问题,请参考以下文章
windows下的IO模型之异步选择(WSAAsyncSelect)模型