异步选择模型(基于消息机制–核心是消息队列)
什么是消息机制?
答:
- Windows处理用户行为的方式之一
- 消息机制的核心是消息队列,所有用户的操作都可以被认为是一个消息,当某些用户动作发生后,其对应的消息就会进入操作系统维护的消息队列;
- 程序员负责将消息队列中的消息取出,并根据消息类型进行分类处理;消息队列是由操作系统维护的;
- 因此消息机制是有序地处理消息队列中的所有消息,早产生的消息早处理!
Windows下的异步选择模型最具有代表性的函数是WSAAsyncSelect()
函数,是基于消息机制条件下的select()
函数的升级版
异步选择模型的逻辑
-
异步选择模型其实是Windows独有的,Win操作系统为每一个窗口维护一个消息队列,这是实现消息机制核心中的核心。不过简单的是,程序员并不需要去创建消息队列,
消息队列的创建
和将新消息装进消息队列
都由操作系统完成。 -
首先应该有一个窗口,因为有了窗口才有消息队列
// 1.创建窗口结构体 WNDCLASSEX // 2.注册窗口结构体 RegisterClassEx // 3.创建窗口 CreateWindowEx // 4.显示窗口 ShowWindow
-
之后就是利用循环,不断地
获取消息
、翻译消息
、分发消息
// 1.获取消息,每次取出消息队列中的头一个消息 GetMessage() // 2.翻译消息 TranslateMessage() // 3.分发消息 DispatchMessage()
-
因为要处理网络连接与通信问题,所以需要创建自定义消息,并将
自定义消息
和套接字socket
以及我们关心的动作
绑定在一起然后投递给操作系统。此时使用的关键函数就是异步选择模型的灵魂所在:WSAAsyncSelect()
预定义的消息都是如
鼠标按下
、键盘按下
、窗口退出
等…而类似于
网络连接
、收发消息
是需要程序员自定义的int WSAAsyncSelect( SOCKET s, // 我们关心的socket,如用于接收连接的服务器socket、与客户端通信的socket HWND hWnd, // 将该消息投递给哪个窗口,也即希望该消息进入哪个窗口的消息队列 u_int wMsg, // 消息号 long lEvent // 某个动作(网络事件)如:FD_ACCEPT FD_READ FD_WRITE FD_CLOSE等 ); // 如何自定义消息呢? #define UM_MYMSG WM_USER + 1 // UM_MYMSG是我自定义的名字 WM_USER之后的消息ID是可以自由指定的
-
还有一个隐含的内容:分发消息之后,程序如何执行呢?其实程序会调用消息响应函数(也可以称为消息回调函数),用来响应不同的消息,如果我们有自定义消息,就需要在该函数中编写相应的分类处理代码。该函数的声明如下:
// 注意GetMessage()每次只会取出一个消息,因此下面的回调函数,每次调用也只是响应一个消息 // 下面的函数,其函数名可变,只要在创建窗口的时候,指定窗口的消息处理回调函数为这个函数名就行 LRESULT CALLBACK WinBackProc( HWND hWnd, // 表示该函数负责处理哪个窗口的消息 UINT msgID, // 指定了消息ID,因此在函数内部就可通过这个ID当前要处理的消息是什么 WPARAM wparam, // 指定了是哪个socket的消息 LPARAM lparam // 指定了具体的事件(行为) 如:FD_ACCEPT FD_READ FD_WRITE FD_CLOSE等 );
因此我们要在该函数中,编写关于
socket接收连接
、收发消息
的代码,也就是所谓的分类处理
如何回答对异步选择模型的理解?
首先异步选择模型基于消息机制,消息机制的核心是消息队列,通常消息队列伴随着windows窗口一起出现,由win操作系统创建和添加新消息进队列。
异步选择模型其实也是基本的select模型的升级版,他和事件选择模型一样,都是解决了select模型中select()
函数执行阻塞的问题,不同在于后者是基于事件机制。
他的核心逻辑就是,将socket
、自定义消息
和具体事件
(如FD_ACCEPT
FD_READ
FD_WRITE
FD_CLOSE
)绑定在一起,然后投递给操作系统去监管,如果相应的消息产生就会被操作系统装进消息队列
我们只需要从消息队列中依次取出每一个消息,在窗口的消息响应函数中获得消息ID、对应的Socket、以及具体事件,然后分类处理即可