异步选择WSAAsyncSelect

来源:互联网 发布:淘宝主图视频内存大小 编辑:程序博客网 时间:2024/05/22 00:35

  大家用这个模型,可以让应用程序在一个套接字上接收以windows消息为基础的网络事件通知。我们想要用这个操作,具体的做法就是我们建立一个套接字,然后调用WSAAsyncSelect函数, 也就是说,这个模型的核心就是我们的这个函数;

来看一下函数原型:

[cpp] view plain copy
  1. int WSAAsyncSelect(  
  2.   _In_  SOCKET s,//我们感兴趣的套接字  
  3.   _In_  HWND hWnd,//窗口的句柄,对于网络事件繁盛后,想要接收到的通知的那个窗口  
  4.   _In_  unsigned int wMsg,//指定在发生网络事件时,打算接收的消息。  
  5.   _In_  long lEvent//指定一个位掩码,对应于一系列网络事件的组合。  
  6. );  

对于最后一个参数,在这里要说一下,他包括的网络事件模型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT、FD_CLOSE。

到底使用FD_ACCEPT,还是使用FD_CONNECT类型,要取决于应用程序的身份是客户端,还是服务器。我们可以用或运算来一起使用;

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

实例:

[cpp] view plain copy
  1. WSAAsyncSeltct(s, hwnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);  
应用程序可以在套接字s上,接收到有关连接,发送,接收以及套接字关闭的一系列通知了。


要注意的是:通过上面的函数原型我们可以看出来,要想使用我们的这个模型,必须要先调用CreateWindow函数来创建一个窗口,然后再为窗口提供一个窗口例程函数(WinProc);


这里要说的注意是:

当你有多个事件的时候,你一定要在套接字上一次注册,一旦你在这个套接字上允许了事件通知,那么以后除非你明确的调用closesocket命令或者由应用程序针对的那个套接字调用了WSAAsyncSelect,那么你就更改了注册的网络事件的类型了,否则的话事件通知就会永远有效。  上面函数你最后一个参数设置为0,效果相当于你停止在套接字上进行的所有网络事件通知。


还有就是你的应用程序针对一个套接字调用了WSAAsyncSelect,那么套接字会从“锁定”模式变成“非锁定”模式。    这样以后,会导致错误。     为了防止错误的产生,应用程序依赖于由WSAAsyncSelect的第三个参数指定的用户自定义窗口消息,来判断网络事件类型何时在套接字上发生,不能盲目的调用。


当你的程序调用WSAAsyncSelect成功后,会在和hWnd窗口句柄对应的窗口以windows消息的形式接受网络事件的通知。

看窗口消息如何定义:

[cpp] view plain copy
  1. LRESULT CALLBACK WindowProc(   
  2.     HWND hwnd,//一个窗口句柄,对窗口的调用就是由这个窗口发出的  
  3.     UINT uMsg,//指定需要对那些消息进行处理。  
  4.     WPARAM wParam,//指定在上面发生了一个网络事件的套接字。  
  5.     LPARAM lParam//低字节制定了已经发生的网络事件,高字节包含了可能出现的错误代码。  
  6. );  

来看一下步骤:当网络消息抵达窗口后,应用程序就会先检查lParam的高字节位,以判断是否是在网络错误。WSAGETSELECTERROR这个宏可返回高字节位包含的错误信息。

然后如果程序没有发现套接字上没产生错误,接着就要调查到底是那个网络事件类型,具体做法是读取lParam的低字节位。  WSAGETSELECTEVENT这个宏返回lParam的低字节部分。


看一下代码:

[cpp] view plain copy
  1. #include<Windows.h>  
  2. #include<tchar.h>  
  3.   
  4. #define MSGSIZE 1024  
  5. #define WM_SOCKET WM_USER+100  
  6.   
  7. #pragma comment(lib, "ws2_32.lib")  
  8.   
  9. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);  
  10.   
  11. int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )  
  12. {  
  13.     TCHAR classname[] = "AsyncSelect";  
  14.   
  15.     WNDCLASS wndclass;   //窗口类  
  16.     wndclass.style = CS_HREDRAW | CS_VREDRAW;//窗口类型  
  17.     wndclass.lpfnWndProc = WindowProc;//窗口处理函数  
  18.     wndclass.cbClsExtra = 0;//窗口类无扩展  
  19.     wndclass.cbWndExtra = 0;//窗口实例无扩展  
  20.     wndclass.hInstance = hInstance;//当前实例句柄  
  21.     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//窗口最小化图标为缺省图标  
  22.     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//窗口用箭头光标  
  23.     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//窗口背景用白色  
  24.     wndclass.lpszMenuName = NULL;//窗口没有菜单  
  25.     wndclass.lpszClassName = classname;//窗口类名为“窗口”  
  26.     //注册窗口类  
  27.     if (!RegisterClass(&wndclass))  
  28.     {  
  29.         MessageBox(NULL, "register class error", classname, MB_ICONERROR);  
  30.         return 0;  
  31.     }  
  32.     //创建窗口(窗口类名,窗口标题,窗口风格,坐标缺省, 有没有父窗口,有没有子窗口, 创建这个窗口的应用程序当前句柄,不适用)  
  33.     HWND hwnd = CreateWindow(classname, "AsyncSelect Model", WS_OVERLAPPEDWINDOW,  
  34.         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,   
  35.         NULL, NULL, hInstance, NULL);  
  36.     //显示和更新  
  37.     ShowWindow(hwnd, nShowCmd);  
  38.     UpdateWindow(hwnd);  
  39.   
  40.     MSG msg;  
  41.     //消息循环  
  42.     while (GetMessage(&msg, NULL, 0, 0))  
  43.     {  
  44.         TranslateMessage(&msg);  
  45.         DispatchMessage(&msg);  
  46.     }  
  47.   
  48.     return msg.wParam;  
  49. }  
  50.   
  51. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
  52. {  
  53.     WSADATA wsd;  
  54.     static SOCKET slisten;  
  55.     SOCKET sClient;  
  56.     SOCKADDR_IN local, client;  
  57.     int ret, iAddrsize = sizeof(client);  
  58.     char szMessage[MSGSIZE] = {0};  
  59.   
  60.     switch (uMsg)  
  61.     {  
  62.     case WM_DESTROY:  
  63.         {  
  64.             //退出清理  
  65.             closesocket(slisten);  
  66.             WSACleanup();  
  67.             PostQuitMessage(0);  
  68.             return 0;  
  69.         }  
  70.     case WM_CREATE:  
  71.         {  
  72.             //初始化  
  73.             WORD sockVersion = MAKEWORD(2, 0);  
  74.             WSAStartup(sockVersion, &wsd);  
  75.             //创建socket  
  76.             slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  77.   
  78.             local.sin_family = AF_INET;  
  79.             local.sin_port = htons(8888);  
  80.             local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
  81.             //绑定socket  
  82.             if(bind(slisten, (sockaddr*)&local, sizeof(local)) == SOCKET_ERROR)  
  83.             {  
  84.                 WSACleanup();  
  85.                 return 0;  
  86.             }  
  87.             //监听socket  
  88.             if(listen(slisten, 5) == SOCKET_ERROR)  
  89.             {  
  90.                 WSACleanup();  
  91.                 return 0;  
  92.             }  
  93.             //注册监听socket FD_ACCEPT事件,当socket有连接时,会发送WM_SOCKET消息给窗口  
  94.             WSAAsyncSelect(slisten, hwnd, WM_SOCKET, FD_ACCEPT);  
  95.             return 0;  
  96.         }  
  97.   
  98.     case WM_SOCKET:  
  99.         {  
  100.             if (WSAGETSELECTERROR(lParam))  
  101.             {  
  102.                 closesocket(wParam);  
  103.                 break;  
  104.             }  
  105.   
  106.             switch(WSAGETSELECTEVENT(lParam))  
  107.             {  
  108.             case FD_ACCEPT:  
  109.                 {  
  110.                     //接受  
  111.                     sClient = accept(wParam, (sockaddr*)&client, &iAddrsize);  
  112.                     //注册客户socket FD_READ和FD_CLOSE事件  
  113.                     WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ|FD_CLOSE);  
  114.                     break;  
  115.                 }  
  116.             case FD_READ:  
  117.                 {  
  118.                     //接受  
  119.                     ret = recv(wParam, szMessage, MSGSIZE, 0);  
  120.   
  121.                     if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)  
  122.                     {  
  123.                         closesocket(wParam);  
  124.                     }  
  125.                     else  
  126.                     {  
  127.                         //发送  
  128.                         szMessage[ret] = '\0';  
  129.                         send(wParam, szMessage, strlen(szMessage), 0);  
  130.                     }  
  131.                     break;  
  132.                 }  
  133.             case FD_CLOSE:  
  134.                 {  
  135.                     closesocket(wParam);  
  136.                     break;  
  137.                 }  
  138.             default:  
  139.                 {  
  140.                     closesocket(wParam);  
  141.                     break;  
  142.                 }  
  143.             }  
  144.         }  
  145.         return 0;  
  146.     }  
  147.   
  148.     return DefWindowProc(hwnd, uMsg, wParam, lParam);  
  149. }  


2012/9/2

jofranks于南昌

0 0
原创粉丝点击