设为首页 收藏本站
查看: 941|回复: 0

[经验分享] Windows socket 之WSAAsyncSelect模型

[复制链接]

尚未签到

发表于 2016-5-24 07:48:46 | 显示全部楼层 |阅读模式
WSAAsyncSelect模型
WSAAsyncSelect模型是Windowssocket的一个异步IO模型。利用该模型可以接收以Windows消息为基础的网络事件。Windowssockets应用程序在创建套接字后,调用WSAAsyncSelect函数注册感兴趣的网络事件,当该事件发生时Windows窗口收到消息,应用程序就可以对接收到的网络时间进行处理。

WSAAsyncSelectselect模型的异步版本。在应用程序使用select函数时会发生阻塞现象。可以通过selecttimeout参数设置阻塞的时间。在设置的时间内,select函数等待,直到一个或多个套接字满足可读或可写的条件。

WSAAsyncSelect是非阻塞的。Windowssockets程序在调用recvsend之前,调用WSAAsyncSelect注册网络事件。WSAAsyncSelect函数立即返回。当系统中数据准备好时,会向应用程序发送消息。此此消息的处理函数中可以调用recvsend进行接收或发送数据。

WSAAsyncSelect模型与select模型的相同点是它们都可以对多个套接字进行管理。但它们也有不小的区别。首先WSAAsyncSelect模型是异步的,且通知方式不同。更重要的一点是:WSAAsyncSelect模型应用在基于消息的Windows环境下,使用该模型时必须创建窗口,而select模型可以广泛应用在Unix系统,使用该模型不需要创建窗口。最后一点区别:应用程序在调用WSAAsyncSelect函数后,套接字就被设置为非阻塞状态。而使用select函数不改变套接字的工作方式。

WSAAsyncSelect函数。
该函数告诉系统当网络事件发生时为套接字发送消息。声明如下:



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



s为需要通知的套接字。
hWnd为当网络事件发生时接收消息的窗口句柄。
wMsg为当网络事件发生时窗口收到的消息。在此消息的响应函数内对网络事件进行处理。
lEvent为应用程序感兴趣的网络事件集合。
应用程序调用该函数后自动将套接字设置为非阻塞模式。通常用户自定义消息应该在WM_USER的基础之上定义。如WM_USER+1,以避免与Windows预定义的消息发生混淆。
网络事件可以有以下几种:

FD_READ:套接字可读通知。
FD_WRITE:可写通知。
FD_ACCEPT:服务器接收连接的通知。
FD_CONNECT:有客户连接通知。
FD_OOB:外带数据到达通知。
FD_CLOSE:套接字关闭通知。
FD_QOS:服务质量发生变化通知。
FD_GROUP_QOS:组服务质量发生变化通知。
FD_ROUTING_INTERFACE_CHANGE:与路由器接口发生变化的通知。
FD_ADDRESS_LIST_CHANGE:本地地址列表发生变化的通知。

开发人员应向应用程序注册感兴趣的网络事件。可以将它们按位或并传给lEvent函数。如:



WSAAsyncSelect(s,hWnd,WM_SOCKET,FD_CONNECT|FD_READ|FD_CLOSE);



上述代码表示:当套接字连接到来、有数据可读或这套接字关闭的网络事件发生时,WM_SOCKET消息就会发送给hWnd为句柄的窗口。
消息处理函数。
消息处理函数是对网络事件发生时窗口消息的处理。它的声明如下:


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



hWnd为窗口句柄。
uMsg为当网络事件发生时的消息。
wParam为消息参数。该参数表明发生网络事件的套接字。
lParam也为消息参数。低字节表明已发生的网络事件。高字节包含错误代码。

Windowssockets应用程序中,当WindowProc接收到网络消息时,在该函数内执行下面的步骤:
1:读取lParam的高字节,判断是否有错误发生。可以使用WSAGETSElECTERROR宏。
2:如果没有错误,读取lParam的低字节,检查发生了什么网络事件,可以使用WSAGETSELECTEVENT宏。

WSAGETSElECTERRORWSAGETSELECTEVENT宏定义如下:


#define WSAGETSElECTERROR(lParam)  LOWORD(lParam)
#define WSAGETSELECTEVENT(lParam)  HIWORD(lParam)



接下来就需要创建窗口和将网络消息与消息处理函数关联起来。如果使用MFC可以使用MFC提供的宏来进行处理。
注意:多次调用WSAAsyncSelect时,最后一次调用会取消前面注册的网络事件。

因为调用accept接受的套接字和监听套接字具有同样的属性。所以,任何为监听套接字设置的网络事件对接受套接字同样起作用。如果一个监听套接字请求FD_ACCEPTFD_READFD_WRITE网络事件。则在该监听套接字上接受的任何套接字也会请求FD_ACCEPTFD_READFD_WRITE网络事件。

FD_CLOSE网络事件用来判断套接字是否已经关闭。错误代码会指出套接字是从容关闭还是硬关闭。如果为0,为从容关闭。若错误代码为WSAECONNRESET,则套接字是硬关闭。调用closesocket不会投递FD_CLOSE事件。
发生网络事件的条件。

下列条件下会发生FD_READ事件:
1:当调用WSAAsyncSelect函数时,如果当前有数据可读。
2:当数据到达并且没有发送FD_READ网络事件时。
3:调用recv()或这recvfrom,如果仍有数据可读里。

下列情况下会发生FD_WRITE事件:
1:调用WSAAsyncSelect函数时,如果能够发送数据时。
2connect或者accept函数后,连接已经建立时。
3:调用send或者sendto函数,返回WSAWOULDBLOCK错误后,再次调用send()或者sendto函数可能成功时。因为此时可能是套接字还处于不可写状态,多次调用直到调用成功为止。

WSAAsyncSelect的优势与不足。

该模型是在基于消息的Windows环境下开发应用程序。开发人员可以像处理其他消息一样,对网络事件进行处理。而且为确保接受所有数据提供了很好的机制。
不足:由于该模型基于Windows消息机制,必须在应用程序中创建窗口。虽然可以在开发中,确定是否显示该窗口。由于调用WSAAsyncSelect函数后自动将套接字设置为非阻塞状态,当应用程序接收到网络事件时,未必能够成功返回。这无疑增加了使用该模型的难度。
接下来展示一个使用如何WSAAsyncSelect模型的例子。该程序使用WSAAsyncSelect模型管理接受的客户端套接字。编码步骤如下:
1:声明自定义消息。如WM_SOCKET
2:声明窗口例程。
3:将自定义消息与消息处理函数相关联。
4:初始化套接字动态库,创建套接字。
5:调用WSAAsyncSelect注册感兴趣的网络事件。本例服务器感兴趣的网络事件有FD_ACCEPTFD_CLOSE
6:绑定套接字开始监听。
一:声明自定义消息:


#define WM_SOCKET WM_USER+1 //套接字消息。

除了声明自定义消息外还需要声明最大字符串长度、服务器监听端口、数据缓冲区。


#define MAX_STRING 100     //最大字符串长度。
#define SERVERPORT 5000    //服务器端口。
#define MAX_SIZE_BUF 1024  //数据缓冲区长度。

二:声明消息处理函数并与消息关联:

1:在窗口类头文件中声明消息处理函数。如:


afx_msg LRESULT onWmSocket(WPARAM wParam, LPARAM lParam);


2:在消息映射宏中将自定义消息如声明的消息处理函数关联:


ON_MESSAGE(WM_SOCKET,&onWmSocket)


3:实现消息处理函数:

LRESULT CuserdefinedMessageTestDlg::onWmSocket( WPARAM wParam, LPARAM lParam )
{
if(WSAGETSELECTERROR(lParam))
{
m_list.deleteNode(wParam);//wParam为发生消息的套接字。出现错误,则从链表中将该套接字对应的CClient类对象删除。
return false;
}
else
{
switch(WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT://接受客户端连接请求。
{
SOCKET sAccept;
if((sAccept==accept(wParam,NULL,NULL)==INVALID_SOCKET))
break;
m_list.add(sAccept);
//在新接受的套接字发生FD_READ,FD_WRITE,FD_CLOSE网络事件发生,发送WM_SOCKET消息;
WSAAsyncSelect(sAccept,this->m_hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
}
break;
case FD_READ://可读,接收数据。
{
CClient *pClinet=GetClient(wParam);//根据套接字,获取客户端节点。
pClient->RecvData();
}
break;
case FD_WRITE://可写,发送数据。
{
CClient*pClient=GetClient(wParam);
pClient->SendData();
}
break;
case FD_CLOSE://对方关闭套接字连接。
{
if(WSAGETSELECTERROR(lParam)==0)
{
//从容关闭。
}
else if(WSAGETSELECTERROR(lParam)==WSAECONNREFUSED)
{
//硬关闭。
}
m_list.deleteNode(wParam);
}
break;
default:
break;
}
}
return 0;
}


三:创建套接字并注册感兴趣的网络事件.
1:初始化套接字动态库,并创建套接字。


WSADATA wsa;
int ret=WSAStartup(MAKEWORD(2,2),&wsa);
ListenSocket=socket(AF_INET,SOCK_STREAM,0);
if(ListenSocket==INVALID_SOCKET)
{
return false;
}


2:注册感兴趣的网络事件:


WSAAsyncSelect(ListenSocket,this,WM_SOCKET,FD_ACCEPT|FD_CLOSE);


3:绑定套接字并监听。


SOCKADDR_IN addr;
addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=inet_addr("192.168.1.100");
addr.sin_port=htons(4000);
int ret=bind(m_ListenSocket,(SOCKADDR*)&addr,sizeof(addr));
if(ret==INVALID_SOCKET)
{
return false;
}
ret=listen(m_ListenSocket,10);
if(ret==INVALID_SOCKET)
{
return false;
}

四:退出


     closesocket(m_ListenSocket);
WSACleanup();




五:CClient类。

自定义类CClient类用于管理服务器接受客户端的新建套接字。在该类中实现与客户端通信。

六;管理客户端套接字链表。
当服务器接受一个客户端连接请求后就会创建一个CClient实例。将该实例地址加入链表中。
以上参考自《精通Windows sockets网络开发--基于Visual C++实现》孙海民著。如有纰漏,请不吝指正!
2012.1.4 于山西大同

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-220834-1-1.html 上篇帖子: Shane 于 2012 在编程哲学、管理、技术、成长之路的新思想 下篇帖子: http://www.cnblogs.com/wujd/archive/2012/01/10/wujiandong-android-1.html
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表