TCP流式套接字的异步事件WSAAsyncSelect编程

503 阅读3分钟

WSAAsyncSelect( )是Winsock提供的一个适合于Windows编程使用的函数,它允许在一个套接口上当发生特定的网络事件时,给Windows网络应用程序(窗口或对话框)发送一个消息(事件通知)。

WSAAsyncSelect函数原型如下:

int WSAAsyncSelect(
    SOCKET s,  //标识一个需要事件通知的套接口描述符
    HWND hWnd,  //标识一个在网络事件发生时要想收到消息的窗口或对话框的句柄
    u_int wMsg,  //在网络事件发生时要接收的消息,该消息会投递到由hWnd句柄指定的窗口或对话框
    long lEvent  //位屏蔽码,用于指明应用程序感兴趣的网络事件集合
);

若应用程序对一个套接口s调用了WSAAsyncSelect( )函数,那么套接口s的模式会自动从阻塞模式变成非阻塞模式。如果应用程序同时对多个网络事件感兴趣,那么只需对各种类型的网络事件执行按位或(OR)的运算即可。进行一次WSAAsyncSelect( )调用,将使为同一个套接口启动的所有以前的WSAAsyncSelect( )调用作废。如果要取消所有的通知,也就是指出Windows Sockets的实现不再在套接口上发送任何和网络事件相关的消息,则把lEvent字段置为0,然后调用WSAAsyncSelect( )。当某一套接口s上发生了一个已命名的网络事件时,应用程序窗口hWnd会接收到消息wMsg。应用程序窗口例程的wParam参数标识了网络事件发生的套接口。lParam参数的低位字指明了发生的网络事件,高位字则含有一个错误代码,该错误代码可以是Winsock2.h中定义的任何错误。

以下为测试WSAAsyncSelect()函数的程序,一个服务器端两个客户端

下面是服务器端程序:

#define WM_SOCKET WM_USER+101 
#include <WINSOCK2.H> 
/*#include <windows.h>*/ 
#pragma comment(lib,"WS2_32") 
//----------------窗口过程函数的声明------------- 
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); 
//----------------WinMain()函数------------------ 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) 
{ 
    WNDCLASS wc; 
    wc.style=CS_HREDRAW|CS_VREDRAW; 
    wc.lpfnWndProc=WindowProc; 
    wc.cbClsExtra=0; 
    wc.cbWndExtra=0; 
    wc.hInstance=hInstance; 
    wc.hIcon=LoadIcon(NULL,IDI_EXCLAMATION); 
    wc.hCursor=LoadCursor(NULL,IDC_ARROW); 
    wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); 
    wc.lpszMenuName=NULL; 
    wc.lpszClassName="Test"; 
    //---注册窗口类---- 
    RegisterClass(&wc); 
    //---创建窗口---- 
    HWND hwnd=CreateWindow("Test","窗口标题",WS_SYSMENU,300,0,600,400,NULL,NULL,hInstance,NULL); 
    if (hwnd==NULL) 
    { 
        MessageBox(hwnd,"创建窗口出错","标题栏提升",MB_OK); 
        return 1; 
    } 
    //---显示窗口---- 
    ShowWindow(hwnd,SW_SHOWNORMAL); 
    UpdateWindow(hwnd); 
    //---socket----- 
    WSADATA wsaData; 
    WORD wVersionRequested=MAKEWORD(2,2); 
    if (WSAStartup(wVersionRequested,&wsaData)!=0) 
    { 
        MessageBox(NULL,"WSAStartup() Failed","调用失败",0); 
        return 1; 
    } 
    SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
    if (s==INVALID_SOCKET) 
    { 
        MessageBox(NULL,"socket() Failed","调用失败",0); 
        return 1; 
    }     
    sockaddr_in sin; 
    sin.sin_family=AF_INET; 
    sin.sin_port=htons(1111); 
    sin.sin_addr.S_un.S_addr=inet_addr("192.168.31.1"); 
    if (bind(s,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR) 
    { 
        MessageBox(NULL,"bind() Failed","调用失败",0); 
        return 1; 
    } 
    if (listen(s,3)==SOCKET_ERROR) 
    { 
        MessageBox(NULL,"listen() Failed","调用失败",0); 
        return 1; 
    } 
    else 
        MessageBox(hwnd,"进入监听状态!","标题栏提示",MB_OK); 
    WSAAsyncSelect(s,hwnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE); 
    //---消息循环---- 
    MSG msg; 
    while (GetMessage(&msg,0,0,0)) 
    { 
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    } 
    closesocket(s); 
    WSACleanup(); 
    return msg.wParam; 
} 
//-------------------窗口过程---------------------- 
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) 
{ 
    switch(uMsg) 
    { 
    case WM_SOCKET: 
        { 
            SOCKET ss=wParam;   //wParam参数标志了网络事件发生的套接口 
            if (WSAGETSELECTERROR(lParam)) 
            { 
                closesocket(ss); 
                return 0; 
            } 
            switch (WSAGETSELECTEVENT(lParam)) 
            { 
            case FD_ACCEPT:   //-----①连接请求到来 
                { 
                     sockaddr_in Cadd; 
                     int Cadd_len=sizeof(Cadd); 
                    SOCKET sNew=accept(ss,(sockaddr*)&Cadd,&Cadd_len); 
                    if (ss==INVALID_SOCKET)                     
                        MessageBox(hwnd,"调用accept()失败!","标题栏提示",MB_OK);                     
                    WSAAsyncSelect(sNew,hwnd,WM_SOCKET,FD_READ|FD_CLOSE); 
                }break; 
            case FD_READ:   //-----②数据发送来 
                { 
                    char cbuf[256]; 
                    memset(cbuf,0,256); 
                    int cRecv=recv(ss,cbuf,256,0); 
                    if ((cRecv==SOCKET_ERROR&& WSAGetLastError() == WSAECONNRESET)|| cRecv==0) 
                    { 
                        MessageBox(hwnd,"调用recv()失败!","标题栏提示",MB_OK); 
                        closesocket(ss); 
                    } 
                    else if (cRecv>0) 
                    { 
                        MessageBox(hwnd,cbuf,"收到的信息",MB_OK);     
                        char Sbuf[]="Hello client!I am server"; 
                        int isend=send(ss,Sbuf,sizeof(Sbuf),0); 
                        if (isend==SOCKET_ERROR || isend<=0) 
                        { 
                            MessageBox(hwnd,"发送消息失败!","标题栏提示",MB_OK);                             
                        } 
                        else 
                            MessageBox(hwnd,"已经发信息到客户端!","标题栏提示",MB_OK); 
                    } 
                }break; 
            case FD_CLOSE:    //----③关闭连接 
                { 
                    closesocket(ss); 
                } 
                break; 
            } 
        } 
        break; 
    case WM_CLOSE: 
        if (IDYES==MessageBox(hwnd,"是否确定退出?","message",MB_YESNO)) 
            DestroyWindow(hwnd); 
        break; 
    case WM_DESTROY: 
        PostQuitMessage(0); 
        break; 
    default: 
        return DefWindowProc(hwnd,uMsg,wParam,lParam); 
    } 
    return 0; 
} 

客户端与上两次的都一样,这里不再给出。详见这里 juejin.cn/post/698095… 先启动服务器端,再启动1号客户端,接着启动2号客户端,就会看到如图所示:

0_127501376341lO.gif