打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
网络编程笔记----WSAAsyncSelect模型--herocao的博客
                                 

Winsock提供了一个有用的异步I / O模型。利用这个模型,应用程序可在一个套接字上,

接收以Windows消息为基础的网络事件通知。具体的做法是在建好一个套接字后,调用

WSAAsyncSelect函数。此模型提供了读写数据能力的异步通知,但不提供异步数据传送。异步数据传送有“重叠及完成端口模型提供”。

消息通知

要想使用WSAAsyncSelect模型,在应用程序中,首先必须用CreateWindow函数创建一个窗口,再为该窗口提供一个窗口例程支持函数( Winproc)。亦可使用一个对话框,为其提供一个对话例程,而非窗口例程,因为对话框本质也是“窗口”。

int WSAAsyncSelect ( 

    SOCKET s,    //要进行操作的SOCKET

     HWND hWnd,  //要绑写的窗口句柄(当事件发生后要接收消息的窗口)

    unsigned int wMsg, //网络事件发生时的的消息响应

long lEvent  //感兴趣的网络事件,请查阅MSDN

);

使用方法可分为:

1初始化套接字相关信息:

 (2) 开始启动一个事件通知。WSAAsyncSelect(Sock, hWnd, 自定义消息, 网络事件)

(3) 响应窗口的自定义消息处理函数, 其中lparam的高位字包含了可能出现的错误

代码,低字节表示发生的网络事件。wParam表示发生网络事件的套接字。

WSAGETSELECTERROR(lParam);  //查看是否出现错误,获取低字节位

WSAGETSELECTEVENT(lParam);  //查看发生了什么事件,获取高字节位

事件种类请查看MSDN,可用WSAGetLastError()来获取错误息。

注意:多个事件必须一次注册完成,一旦在某个套接字上启用了事件通知,注册过的事件将一直有效,除非明确调用closesocket命令,或针对这个套接字再调用WSAAsyncSelect更改注册过的网络事件。

优点:可在系统开销不大的情况下同时处理许多连接。

缺点:即使用不需要窗口(如服务器,控制台)它也不得不额外使用一个窗口。同时如果处理成千上万套接字的所有事件,性能可想而知。

MFCCSocket所使用的正是这种事件通知模型

今天看了一下,消息通知的几个类型。用的比较多的可能就是FD_SEND,FD_WRITE,在网上查了一下资料,总结如下:

FD_WRITE 事件】

1)呼叫 WSAAsyncSelect() 来设定 FD_WRITE 事件时,Socket 已经可以传送资料(TCP scoket 已经和对方连接成功了,或 UDP socket 已建立完成),且目前 output buffer 仍有空间可写入资料。
2)呼叫 WSAAsyncSelect() 来设定 FD_WRITE 事件时,Socket 尚不能传送资料,不过一旦 Socket 与对方连接成功,马上就会收到 FD_WRITE 的通知。
3)呼叫 send() sendto() 传送资料时,系统告知错误,且错误码为10035 WSAEWOULDBLOCK (呼叫 WSAGetLastError() 得知这项错误),这时表示 output buffer 已经满了,无法再写入任何资料(此时即令呼叫再多次的 send() 也都一定失败);一旦系统将部份资料成功送抵对方,空出 output buffer 后,便会送一个 FD_WRITE 给使用者,告知可继续传送资料了。换句话说,读者在呼叫 send() 传送资料时,只要不是返回错误 10035 的话,便可一直继续呼叫 send() 来传送资料;一旦 send() 回返错误 10035,那麽便不要再呼叫 send() 传送资料,而须等收到 FD_WRITE 后,再继续传送资料。

如果只是发送很少的数据,可能只出现第二种情况,所以在发送少量数据的时候,不要使用FD_WRITE机制。

FD_READ 事件】

1)呼叫 WSAAsyncSelect 函式来对此 Socket 设定 FD_READ 事件时,input buffer 中已有资料。
2)原先系统的 input buffer 是空的,当系统再收到资料时,会通知我们。

3)使用者呼叫 recv recvfrom函式,从 input buffer 读取资料,但是并没有一次将资料读光,此时会再驱动一个 FD_READ 事件,表示仍有资料在 input buffer 中。

     读者必须注意:如果我们收到 FD_READ 事件通知的讯息,但是我们故意不呼叫 recv recvfrom 来读取资料的话,尔後系统又收到资料时,并不会再次通知我们,一定要等我们呼叫了 recv recvfrom 後,才有可能再收到FD_READ 的事件通知。

 

 

下面是一个利用WSAAsyncSelect的小程序:

    WORD wVersionRequested;

    WSADATA wsaData;

    int err;

   

    wVersionRequested = MAKEWORD( 2, 2 );

    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 )

    {

        return;

    }

    if ( LOBYTE( wsaData.wVersion ) != 2 ||

        HIBYTE( wsaData.wVersion ) != 2 )

    {

        WSACleanup( );

        return;

span lang="EN-US" style="LINE-HEIGHT: 150%; FONT-FAMILY: 宋体; mso-bidi-font-size: 10.5pt; mso-font-kerning: 0pt; mso-bidi-font-family: AdobeSongStd-Light-Acro">    }

 

 

    char hostname[256];

    gethostname(hostname,sizeof(hostname));                     //这一代码是为

    HOSTENT*hos=gethostbyname(hostname);                        //了实现自动获取安

    CString CS=inet_ntoa(*(struct in_addr*)hos->h_addr_list[0]);// 装程序的主机代码

   

    SOCKET serverSocket;

    serverSocket = socket(AF_INET, SOCK_STREAM, 0);

    SOCKADDR_IN addr;

    addr.sin_family = AF_INET;

    addr.sin_port = htons(6000);

    addr.sin_addr.S_un.S_addr = inet_addr(CS);

   

    bind(serverSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR));

    //注册网络事件

    if(SOCKET_ERROR == WSAAsyncSelect(serverSocket, m_hWnd, WM_NETMESSAGE, FD_ACCEPT|FD_CLOSE))

    {

        MessageBox("注册事件出错!");

        closesocket(serverSocket);

        WSACleanup();

        return;

    }

    listen(serverSocket, 5);

}

void CAsyselectsrvDlg::Onrecvmessage(WPARAM wParam, LPARAM lParam)

{

    if(WSAGETSELECTERROR(lParam))

    {

        AfxMessageBox("接受消息错误");

        closesocket(wParam);

        return;

    }

    switch(WSAGETSELECTEVENT(lParam))

    {

    case FD_ACCEPT:

 

 

        acceptclient(wParam, m_hWnd);

        break;

    case FD_WRITE:

        MessageBox("write");

        break;

    case FD_READ:

        MessageBox("read");

        break;

    case FD_CLOSE:

        closesocket(wParam);

        break;

    default:

        break;

       

    }

    return;

}

void acceptclient(SOCKET sock, HWND hwnd)

{

    SOCKET acceptclient;

    SOCKADDR_IN clientaddr;

    int len = sizeof(SOCKADDR_IN);

    acceptclient = accept(sock, (struct sockaddr*)&clientaddr, &len);

    if(SOCKET_ERROR == WSAAsyncSelect(acceptclient, hwnd, WM_NETMESSAGE, FD_READ|FD_WRITE|FD_CLOSE))

    {

        AfxMessageBox("注册消息出错!");

        closesocket(sock);

        closesocket(acceptclient);

        WSACleanup();

        return;

    }

}

这段程序可以正常运行,前面定义全局函数:void acceptclient(SOCKET sock, HWND hwnd);

今晚就写到这里了。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
几种winsock I/O模型的分析(全面分析)
socket异步笔记
Windows Sockets API实现网络异步通讯 | VC | 软件 | 天极Yes...
winsock中的I/O模型
计算机网络及Windowssocket网络编程
Windows Socket编程简介_网络技术_程序开发_远播教育网
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服