WSAAsyncSelect模型是winsock编程模型的一种,它提供了socket异步编程的方便,其实现是基于Windows消息机制的,最主要的就是下面这个函数:

1 int PASCAL FAR WSAAsyncSelect (SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);

s 标识一个需要事件通知的套接口的描述符.
hWnd 标识一个在网络事件发生时需要接收消息的窗口句柄.
wMsg 在网络事件发生时要接收的消息.
lEvent位屏蔽码,用于指明应用程序感兴趣的网络事件集合.

1、服务器端

因此首先需要自定义一个消息

#define WM_SOCKET WM_USER + 1

并定义消息处理函数,以及做映射

1 // 声明消息处理函数
2 afx_msg LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
3 // 映射
4 ON_MESSAGE(WM_SOCKET,OnSocket)

初始化socket

 1 BOOL CSocketSeverDlg::InitNetwork()
 2 {
 3     WSADATA wsaData;
 4     //初始化TCP协议
 5     BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData);
 6     if(ret != 0)
 7     {
 8         MessageBox("初始化网络协议失败!");
 9         return FALSE;
10     }
11     //创建服务器端套接字
12     ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
13     if(ServerSock == INVALID_SOCKET)
14     {
15         MessageBox("创建套接字失败!");
16         closesocket(ServerSock);
17         WSACleanup();
18         return FALSE;
19     }
20     //绑定到本地一个端口上
21     sockaddr_in localaddr;
22     localaddr.sin_family = AF_INET;
23     localaddr.sin_port = htons(8888); file://端口号不要与其他应用程序冲突
24     localaddr.sin_addr.s_addr = 0;
25     if(SOCKET_ERROR = = bind(ServerSock ,(struct sockaddr*)&localaddr,sizeof(sockaddr)))
26     {
27         MessageBox("绑定地址失败!");
28         closesocket(ServerSock);
29         WSACleanup();
30         return FALSE;
31     }
32     //将SeverSock设置为异步非阻塞模式,并为它注册各种网络异步事件,其 中 m_hWnd
33     //为应用程序的主对话框或主窗口的句柄
34     if(SOCKET_ERROR == WSAAsyncSelect(ServerSock, m_hWnd, WM_SOCKET,
35     FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE))
36     {
37         MessageBox("注册网络异步事件失败!");
38         WSACleanup();
39         return FALSE;
40     }
41     //设置侦听模式
42     listen(ServerSock, 5);
43     return TRUE;
44 }

定义自定义消息处理函数

 1 LRESULT CSocketSeverDlg::OnSocket(WPARAM wParam, LPARAM lParam)
 2 {
 3     //调用Winsock API函数,得到网络事件类型
 4     int iEvent = WSAGETSELECTEVENT(lParam);
 5     //调用Winsock API函数,得到发生此事件的客户端套接字
 6     SOCKET CurSock= (SOCKET)wParam;
 7     switch(iEvent)
 8     {
 9         case FD_ACCEPT:
10             //客户端连接请求事件
11             OnAccept(CurSock);
12             break;
13         case FD_CLOSE:
14             //客户端断开事件:
15             OnClose(CurSock);
16             break;
17         case FD_READ:
18             //网络数据包到达事件
19             OnReceive(CurSock);
20             break;
21         case FD_WRITE:
22             //发送网络数据事件
23             OnSend(CurSock);
24             break;
25         default: break;
26     }
27         return 0;
28 }

2、客户端

用同样的方法建立一个客户端应用程序,初始化网络部分,不需要将套接字设置为监听模式。注册网络事件时没有FD_ACCEPT,但增加了FD_CONNECT事件,因此没有OnAccept函数,但增加了OnConnect函数,向服务器发出连接请求时,使用connect函数,连接成功后,会相应到OnConnect函数中。下面是OnConnect函数的定义,传进来的参数是客户端Socket和服务器端发回来的连接是否成功的标识。

 1 void CSocketClientDlg::OnConnect(SOCKET CurSock, int error)
 2 {
 3     if(0 == error)
 4     {
 5         if(CurSock == ClientSock)
 6         {
 7             MessageBox("连接成功!");
 8         }
 9     }
10 }

定义OnReceive函数,处理网络数据到达事件

定义OnSend函数,处理发送网络数据事件

定义OnClose函数,处理服务器socket关闭事件

以上就是用基于windows消息机制的异步I/O模型实现服务器/客户端应用程序的基本方法。

3、我的疑问

我有一点不是很明白,那就是这个发送网络数据事件是什么意思?什么时候发生?是我们使用send函数发送数据时发生么,

 

未完待续。。。