在WSAAsyncSelect模型中,应用程序可以在一个Socket上接收以Windows消息为基础的网络事件通知,它实现了读写数据的异步通知功能,但不提供异步的数据传送。

1、WSAAsyncSelect()函数
WSAAsyncSelect模型的核心是WSAAsyncSelect()函数,它可以使Windows程序接收网络事件消息。

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

s:事件通知所需要的Socket;
hWnd:网络事件发生时接受消息的窗口句柄;
wMsg:网络事件发生时接收的消息;
lEvent:感兴趣的网络事件;
同时函数调用时自动将Socket s设置为非阻塞模式。

2、创建窗口
在使用WSAAsyncSelect前,必须先调用CreateWindow函数创建一个窗口,再为该窗口提供一个窗口例程支持函数WindowProcCALLBACK供系统调用)。调用CreateWindow函数前,需要调用RegisterClassEx()函数苏注册窗口类,并关联例程函数。
(1)注册窗口类

char classname[]="Mainclass";
WNDCLASSEX wndclass;
wndclass.cbSize=sizeof(wndclass);
wndclass.style=CS_HREDRAW|CS_VREDRAW;
wndclass.lpfnWndProc=WindowProc;         // 窗口例程
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=NULL;
wndclass.hIcon=LoadIcon(NULL,IDI_WARNING);
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=classname;
wndclass.hIconSm=NULL;
RegisterClassEx(&wndclass);             // 注册窗口类

(2)创建主窗口

HWND hwnd=CreateWindowEx(
        0,
        classname,
        "",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        NULL,
        NULL);
return hwnd;

返回值hwnd作为接收消息的窗口句柄。

3、窗口例程
窗口例程用于以消息形式接收网络事件通知,它是一个回调函数(CALLBACK),创建成功之后供系统调用。
函数原型如下:

LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPRAM wParam,LPARAM lParam);

hWnd:窗口句柄;
uMsg:自定义消息;
wParam:消息参数,指定网络事件Socket;
lParam:消息参数,高字节包含可能出现的错误,低字节指定已经发生的网络事件;
消息抵达窗口例程,应用程序首先检查lParam的高字节,以判断是否在Socket上发生了一个网络错误,使用WSAGETSELECTERROR返回高字节的错误信息;如果没有错误,通过WSAGETSELECTEVENT读取低字节内容。

LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    if(uMsg==WM_SOCKET)
    {
        if(WSAGETSELECTERROR(lParam))
        {
            closesocket(wParam);
            return 0;
        }

        switch(WSAGETSELECTEVENT(lParam))
        {
        case FD_ACCEPT:
            {
                SOCKET client=accept(wParam,NULL,NULL);
                // 监听客户端程序
                WSAAsyncSelect(client,hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
            }

            break;

        case FD_WRITE:
            break;

        case FD_READ:
            {
                ZeroMemory(buf,BUF_SIZE);
                int ret=recv(wParam,buf,BUF_SIZE,0);
                buf[ret]='\0';
                if(ret>0)
                    cout<<buf<<endl;
                else
                    closesocket(wParam);
            }
            break;

        case FD_CLOSE:
            closesocket(wParam);
        }

        return 0;
    }
    else if(uMsg==WM_DESTROY)
    {
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

注:WM_SOCKET是我们自定的消息,WIndows规定小于WM_USER是系统消息,大于WM_USER是用户自定义消息,#define WM_SOCKET (WM_USER+10).

4、主程序

MSG msg;
int ret;
while((ret=GetMessage(&msg,NULL,0,0))!=0)
{
    if(ret!=-1)
    {
        // 虚拟键消息转化为字符串消息
        TranslateMessage(&msg);
        // 分发消息、交给WindowProc
        DispatchMessage(&msg);
    }
}

主函数中启动消息循环,GetMessage->TranslateMessage->DispatchMessage

标签: none

评论已关闭