windows 程序设计 套接字异步选择

消息机制-WSAAsyncSelect


WSAAsyncSelect基于Windows消息机制异步I/O模型,为特定网络事件指定系统通知信息.
函数定义
int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);

(1)参数4-1Event:设定用户关心得套接口上的网络事件.形式如:1Event=FD_READ/FD_CLOSE.

网络I/O事件 简表

事件值                             含义                                  触发条件
FD_READ          套接口有可读消息通知                      recv,recvfrom,WSARecv或WSARecvFrom
FD_WRITE         套接口有可发消息通知                      send,sendto,WSASend或WSASendTo
FD_OOB           套件口有外带数据消息通知                recv,recvfrom,WSARecv或WSARecvFrom
FD_ACCEPT      套接口有链接请求消息通知                accept或WSAAccept(错误码不能WSATRY_AGAIN)
FD_CONNECT 希望得到connect或多点join操作完成信息通知     无
FD_CLOSE(面向连接) 套接口关闭消息通知                                  无
FD_QOS           套接口QOS状态发生变化消息通知      WSAIoctl(SIO_GET_QOS)
FD_GROUP_QOS 保留                                              Reserved
FD_ROUTING_INTERFACE_CHANGE                         
特定方向的路由接口发生改变消息通知。                      WSAIoctl(SIO_ROUTING_INTERFACE_CHANGE)
FD_ADDRESS_LIST_CHANGE
得到本地地址列表上套接口协议族发生改变通知。         WSAIoctl(SIO_ADDRESS_LIST_CHANGE)


(2)参数3-wMsg:为套接口事件设定1Event通知消息通常形式(WM_USER + n)
如 #define WM_USER_SERVER     WM_USER+1

(3)参数2-bWnd指定系统通知消息wMsg的窗口句柄.函数若调用成功,
WSAAsyncSelect返回0;否则返回SOCKET_ERROR,这时可用WSAGetLastError来获取错误码. 
事实上,成功发送一次消息,通知机制会暂停工作直到有重新激活消息通知机制到来.才可重新激活事件.

水平触发:FD_READ,FD_OOB和FD_ACCEPT 即重新激活,需要引发消息发送的条件. 
边缘触发:FD_QOS,FD_GROUP_QOS等.

同一接口多次调用WSAAsyncSelect函数,只有最后一次生效.
因此:
WSAAsyncSelect(s, m_hWnd, WM_USER_SERVER, FD_READ),加上
WSAAsyncSelect(s, m_hWnd, WM_USER_SERVER, FD_CLOSE) 并不等于
WSAAsyncSelect(s, m_hWnd, WM_USER_SERVER, FD_READ | FD_CLOSE)

取消套接口上的I/O事件消息通知:、
WSAAsyncSelect(s, hWnd, 0, 0)

在MFC编程环境中,使用该消息处理函数

BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)
     //{{AFX_MSG_MAP(CXXXDlg)
     ........
     //}}AFX_MSG_MAP
     ON_MESSAGE(WM_USER_SERVER, OnServerMsg)
     ........
END_MESSAGE_MAP()

处理消息函数声明为:
afx_msg void OnSERVERMsg(WPARAM wparam,LPARAM lParam);
它定义为:
void CXXXDlg::OnServerMsg(WPARAM wParam,LPARAM lParam)
{
       SOCKET sock = (SOCKET) wParam;
       if(WSAGETSELECTERROR(lParam)){
             ErrorProcess();
             return;
}

switch(WSAGETSELECTEVENT(lParam)){
case FD_READ:
          ReadDate(sock);    // 读数据并进行相应处理
          break;
case FD_CLOSE:
          Finsish(sock);        //套接口关闭工作
          break;
default;
          break;
}
}

其中函数会接受到两个参数wParam 和 lParam。
其中 wParam参数 指明网络事件套接口,多个套接口指定同一个用户消息,那么就需要根据wParam判断到底是哪个套接口待处理。
lParam参数包含两信息:底字指定发生网络事件,高字包含可能出现错误带代码。
另外 OnServerMsg函数中出现两个宏定义
#define WSAGETSELECTERROR(lParam)     HIWORD(lParam)
#define WSAGETSELECTEVENT(lparam)     LOWORD(lParam)
一般来讲,当用户 收到第一个FD_WRITE用户消息时就可以向套接口写数据,直到碰到WSAEWOULBALOCK错误时才
等待下一次FD_WRITE通知消息。

总结:WSAAsynSelect在MFC中使用流程
(1)、使用#define语句定义套接口网络事件设置用户消息值,一般为WM_USER+N形式。
(2)、调用WSAAsynsSelect函数,为套接口设定"网络事件-用户消息-消息接收窗体"的对应关系。
(3)、在消息接收窗体的代码的消息映射模块中,加入ON_MESSAGE宏,设定用户消息的处理函数。
(4)、编写用户处理函数,该函数应该首先使用WSAGETSELECTERROR宏判断是否有错误发生:
然后根据wParam值了解是哪一个套接口上发生了网络事件从而引起用户消息被发送:最后使用
WSAGETSELECTEVENT宏来了解所发生的网络事件,从而进行相应处理。







/*-------------------------------------------------------
   NETTIME.C -- Sets System Clock from Internet Services
                (c) Charles Petzold, 1998
  -------------------------------------------------------*/

#include <windows.h>
#include "resource.h"

#define WM_SOCKET_NOTIFY (WM_USER + 1)
#define ID_TIMER         1

LRESULT CALLBACK WndProc   (HWND, UINT, WPARAM, LPARAM) ;
BOOL    CALLBACK MainDlg   (HWND, UINT, WPARAM, LPARAM) ;
BOOL    CALLBACK ServerDlg (HWND, UINT, WPARAM, LPARAM) ;

void ChangeSystemTime (HWND hwndEdit, ULONG ulTime) ;
void FormatUpdatedTime (HWND hwndEdit, SYSTEMTIME * pstOld, 
                                       SYSTEMTIME * pstNew) ;
void EditPrintf (HWND hwndEdit, TCHAR * szFormat, ...) ;

HINSTANCE hInst ;
HWND      hwndModeless ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("NetTime") ;
     HWND         hwnd ;
     MSG          msg ;
     RECT         rect ;
     WNDCLASS     wndclass ;

     hInst = hInstance ;

     wndclass.style         = 0 ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = NULL ;
     wndclass.hbrBackground = NULL ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("Set System Clock from Internet"),
                          WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
                               WS_BORDER | WS_MINIMIZEBOX,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

          // Create the modeless dialog box to go on top of the window

     hwndModeless = CreateDialog (hInstance, szAppName, hwnd, MainDlg) ;

          // Size the main parent window to the size of the dialog box.  
          //   Show both windows.

     GetWindowRect (hwndModeless, &rect) ;
     AdjustWindowRect (&rect, WS_CAPTION | WS_BORDER, FALSE) ;// 计算矩形窗口大小

     SetWindowPos (hwnd, NULL, 0, 0, rect.right - rect.left,
                   rect.bottom - rect.top, SWP_NOMOVE) ;

     ShowWindow (hwndModeless, SW_SHOW) ;     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;

          // Normal message loop when a modeless dialog box is used.

     while (GetMessage (&msg, NULL, 0, 0))
     {
          if (hwndModeless == 0 || !IsDialogMessage (hwndModeless, &msg))
          {
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
          }
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     switch (message)
     {
     case WM_SETFOCUS:
          SetFocus (hwndModeless) ;
          return 0 ;

     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

BOOL CALLBACK MainDlg (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static char   szIPAddr[32] = { "132.163.4.101" } ;
     static HWND   hwndButton, hwndEdit ;
     static SOCKET sock ;
     static struct sockaddr_in sa ;
     static TCHAR  szOKLabel[32] ;
     int           iError, iSize ;
     unsigned long ulTime ;
     WORD          wEvent, wError ;
     WSADATA       WSAData ;     
 
     switch (message)
     {
     case WM_INITDIALOG:
          hwndButton = GetDlgItem (hwnd, IDOK) ;
          hwndEdit = GetDlgItem (hwnd, IDC_TEXTOUT) ;
          return TRUE ;

     case WM_COMMAND:
          switch (LOWORD (wParam))
          {
          case IDC_SERVER:
               DialogBoxParam (hInst, TEXT ("Servers"), hwnd, ServerDlg, 
                               (LPARAM) szIPAddr) ;
               return TRUE ;

          case IDOK:
                    // Call "WSAStartup" and display description text
				// 初始化套件子库
               if (iError = WSAStartup (MAKEWORD(2,0), &WSAData))
               {
                    EditPrintf (hwndEdit, TEXT ("Startup error #%i.\r\n"), 
                                          iError) ;
                    return TRUE ;
               }
               EditPrintf (hwndEdit, TEXT ("Started up %hs\r\n"), 
                                     WSAData.szDescription);

                    // Call "socket"
				// 创建一个套接字
               sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ;

               if (sock == INVALID_SOCKET)
               {
                    EditPrintf (hwndEdit, 
                                TEXT ("Socket creation error #%i.\r\n"), 
                                WSAGetLastError ()) ;
                    WSACleanup () ;
                    return TRUE ;
               }
               EditPrintf (hwndEdit, TEXT ("Socket %i created.\r\n"), sock) ;

                    // Call "WSAAsyncSelect" 调用异步选择事件 接收连接和读数据

               if (SOCKET_ERROR == WSAAsyncSelect (sock, hwnd, WM_SOCKET_NOTIFY, 
                                                   FD_CONNECT | FD_READ))
               {
                    EditPrintf (hwndEdit, 
                                TEXT ("WSAAsyncSelect error #%i.\r\n"),
                                WSAGetLastError ()) ;
                    closesocket (sock) ;
                    WSACleanup () ;
                    return TRUE ;
               }

                    // Call "connect" with IP address and time-server port

               sa.sin_family           = AF_INET ;// 必须是这个标志
               sa.sin_port             = htons (IPPORT_TIMESERVER) ; // 端口
               sa.sin_addr.S_un.S_addr = inet_addr (szIPAddr) ; // IP地址

			   //连接
               connect(sock, (SOCKADDR *) &sa, sizeof (sa)) ;

                    // "connect" will return SOCKET_ERROR because even if it
                    // succeeds, it will require blocking. The following only
                    // reports unexpected errors.

               if (WSAEWOULDBLOCK != (iError = WSAGetLastError ()))
               {
                    EditPrintf (hwndEdit, TEXT ("Connect error #%i.\r\n"), 
                                          iError) ;
                    closesocket (sock) ;
                    WSACleanup () ;
                    return TRUE ;
               }
               EditPrintf (hwndEdit, TEXT ("Connecting to %hs..."), szIPAddr) ;
     
                    // The result of the "connect" call will be reported 
                    // through the WM_SOCKET_NOTIFY message.
                    // Set timer and change the button to "Cancel"

               SetTimer (hwnd, ID_TIMER, 1000, NULL) ;
               GetWindowText (hwndButton, szOKLabel, sizeof (szOKLabel) /
                                                     sizeof (TCHAR)) ;
               SetWindowText (hwndButton, TEXT ("Cancel")) ;
               SetWindowLong (hwndButton, GWL_ID, IDCANCEL) ;
               return TRUE ;

          case IDCANCEL:
               closesocket (sock) ;
               sock = 0 ;
               WSACleanup () ;
               SetWindowText (hwndButton, szOKLabel) ;
               SetWindowLong (hwndButton, GWL_ID, IDOK) ;

               KillTimer (hwnd, ID_TIMER) ;
               EditPrintf (hwndEdit, TEXT ("\r\nSocket closed.\r\n")) ;
               return TRUE ;

          case IDC_CLOSE:
               if (sock)
                    SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ;

               DestroyWindow (GetParent (hwnd)) ;
               return TRUE ;
          }
          return FALSE ;

     case WM_TIMER:
          EditPrintf (hwndEdit, TEXT (".")) ;
          return TRUE ;

     case WM_SOCKET_NOTIFY:
          wEvent = WSAGETSELECTEVENT (lParam) ;   // ie, LOWORD  事件标志
          wError = WSAGETSELECTERROR (lParam) ;   // ie, HIWORD  错误标志

               // Process two events specified in WSAAsyncSelect

          switch (wEvent)
          {
               // This event occurs as a result of the "connect" call

          case FD_CONNECT:
               EditPrintf (hwndEdit, TEXT ("\r\n")) ;

               if (wError)
               {
                    EditPrintf (hwndEdit, TEXT ("Connect error #%i."), 
                                          wError) ;
                    SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ;
                    return TRUE ;
               }
               EditPrintf (hwndEdit, TEXT ("Connected to %hs.\r\n"), szIPAddr) ;

                    // Try to receive data. The call will generate an error
                    // of WSAEWOULDBLOCK and an event of FD_READ

               recv (sock, (char *) &ulTime, 4, MSG_PEEK) ;// 查看下socket 的数据
               EditPrintf (hwndEdit, TEXT ("Waiting to receive...")) ;
               return TRUE ;

                    // This even occurs when the "recv" call can be made
               
          case FD_READ:
               KillTimer (hwnd, ID_TIMER) ;
               EditPrintf (hwndEdit, TEXT ("\r\n")) ;

               if (wError)
               {
                    EditPrintf (hwndEdit, TEXT ("FD_READ error #%i."), 
                                          wError) ;
                    SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ;
                    return TRUE ;
               }
                    // Get the time and swap the bytes

               iSize = recv (sock, (char *) &ulTime, 4, 0) ;
               ulTime = ntohl (ulTime) ;// 网络字节序转本地字节序
               EditPrintf (hwndEdit, 
                           TEXT ("Received current time of %u seconds ")
                           TEXT ("since Jan. 1 1900.\r\n"), ulTime) ;// 1900.1.1 - 现在的秒数

                    // Change the system time
     
               ChangeSystemTime (hwndEdit, ulTime) ;
               SendMessage (hwnd, WM_COMMAND, IDCANCEL, 0) ;
               return TRUE ;
          }
          return FALSE ;
     }
     return FALSE ;
}

BOOL CALLBACK ServerDlg (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static char * szServer ;
     static WORD   wServer = IDC_SERVER1 ;
     char          szLabel [64] ;

     switch (message)
     {
     case WM_INITDIALOG:
          szServer = (char *) lParam ;
          CheckRadioButton (hwnd, IDC_SERVER1, IDC_SERVER10, wServer) ;
          return TRUE ;

     case WM_COMMAND:
          switch (LOWORD (wParam))
          {
          case IDC_SERVER1:
          case IDC_SERVER2:
          case IDC_SERVER3:
          case IDC_SERVER4:
          case IDC_SERVER5:
          case IDC_SERVER6:
          case IDC_SERVER7:
          case IDC_SERVER8:
          case IDC_SERVER9:
          case IDC_SERVER10:
               wServer = LOWORD (wParam) ;
               return TRUE ;

          case IDOK:
               GetDlgItemTextA (hwnd, wServer, szLabel, sizeof (szLabel)) ;
               strtok (szLabel, "(") ;
               strcpy (szServer, strtok (NULL, ")")) ;
               EndDialog (hwnd, TRUE) ;
               return TRUE ;

          case IDCANCEL:
               EndDialog (hwnd, FALSE) ;
               return TRUE ;
          }
          break ;
     }
     return FALSE ;
}

void ChangeSystemTime (HWND hwndEdit, ULONG ulTime)
{
     FILETIME      ftNew ;
     LARGE_INTEGER li ;
     SYSTEMTIME    stOld, stNew ;

     GetLocalTime (&stOld) ;// 得到本地时间

     stNew.wYear         = 1900 ;
     stNew.wMonth        = 1 ;
     stNew.wDay          = 1 ;
     stNew.wHour         = 0 ;
     stNew.wMinute       = 0 ;
     stNew.wSecond       = 0 ;
     stNew.wMilliseconds = 0 ;

     SystemTimeToFileTime (&stNew, &ftNew) ;
     li = * (LARGE_INTEGER *) &ftNew ;
     li.QuadPart += (LONGLONG) 10000000 * ulTime ; // 100纳秒为单位 * 距离1900.1.1 的秒数
     ftNew = * (FILETIME *) &li ;
     FileTimeToSystemTime (&ftNew, &stNew) ;

     if (SetSystemTime (&stNew))
     {
          GetLocalTime (&stNew) ;
          FormatUpdatedTime (hwndEdit, &stOld, &stNew) ;
     }
     else
          EditPrintf (hwndEdit, TEXT ("Could NOT set new date and time.")) ;
}

void FormatUpdatedTime (HWND hwndEdit, SYSTEMTIME * pstOld, SYSTEMTIME * pstNew)
{
     TCHAR szDateOld [64], szTimeOld [64], szDateNew [64], szTimeNew [64] ;

     GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE,
                    pstOld, NULL, szDateOld, sizeof (szDateOld)) ;
     
     GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | 
                         TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
                    pstOld, NULL, szTimeOld, sizeof (szTimeOld)) ;

     GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE,
                    pstNew, NULL, szDateNew, sizeof (szDateNew)) ;
     
     GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | 
                         TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
                    pstNew, NULL, szTimeNew, sizeof (szTimeNew)) ;

     EditPrintf (hwndEdit, 
                 TEXT ("System date and time successfully changed ")
                 TEXT ("from\r\n\t%s, %s.%03i to\r\n\t%s, %s.%03i."), 
                 szDateOld, szTimeOld, pstOld->wMilliseconds,
                 szDateNew, szTimeNew, pstNew->wMilliseconds) ;
}

void EditPrintf (HWND hwndEdit, TCHAR * szFormat, ...)
{
     TCHAR   szBuffer [1024] ;
     va_list pArgList ;

     va_start (pArgList, szFormat) ;
     wvsprintf (szBuffer, szFormat, pArgList) ;
     va_end (pArgList) ;

     SendMessage (hwndEdit, EM_SETSEL, (WPARAM) -1, (LPARAM) -1) ;
     SendMessage (hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) szBuffer) ;
     SendMessage (hwndEdit, EM_SCROLLCARET, 0, 0) ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值