




已阅读5页,还剩59页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
WSASsyncSelect模型,前面学习的Windows Sockets select模型。在应用程序中调用select()函数时,会发生阻塞现象。开发人员可以通过select()函数的timeout参数,设置函数调用的阻塞时间,在设定时间内,线程保持等待,直到其中的一个或者多个套接字满足可读或者可写的条件,该函数才返回。,WSAAsyncSelect模型是非阻塞的。如图所示,Windows Sockets 应用程序在调用recv()函数接收数据前,调用WSAAsyncselect()函数注册网络事件。WSAAsyncselect()函数立即返回,线程继续运行。当系统中数据准备好时,向应用程序发送消息。应用程序接收到这个消息后,调用recv()函数接收数据。,与select模型比较,WSAAsyncSelect模型与Select模型的相同点: 都可以对Windows套接字应用程序所使用的多个套接字进行有效的管理。 WSAAsyncSeelect模型与Select模型相比存在以下不同: WSAAsyncSelet模型是异步的。在应用程序中调用WSAAsyncSelect()函数,通知系统感兴趣的网络事件,该函数立即返回,应用程序继续运行。,发生网络事件时,应用程序得到通知的方式不同。Select()函数返回时,说明某个或者某些套接字满足可读可写的条件,应用程序需要使用FD_ISSET宏,判断套接字是否存在于可读可写集合中。而对于WSAAsyncSelect模型来说,当网络事件发生时,系统向应用程序发送消息。,WSAAsyncSelect模型应用在基于消息的Windows环境下,使用该模型时必须创建窗口。而Slelect模型广泛应用在Unix系统和Windows系统,使用模型不需要创建窗口。 应用程序中调用WSAAsyncSelect()函数后,自动将套接字设置为非阻塞模式。而应用程序中调用select()函数后,并不能改变该套接字的工作方式。,套接字WSAAsyncSelect模型实现,WSAAsyncSelect模型核心是WSAAsyncSelect()函数,该函数使得Windows应用程序能够接收网络事件消息。在应用程序窗口例程中对接收到的网络事件进行处理。 由于WSAAsyncSelect模型应用在基于消息的Windows应用程序中,所以本节还将讲解窗口例程和如何创建窗口等内容。,WSAAsynSelect()函数,int WSAAsyncSelect( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent ),s:需要事件通知的套接字。 hWnd: 当网络事件发生时接收消息的窗口句柄。 wWsg: 当网络事件发生时窗口收到的消息。 lEvent:应用程序感兴趣的网络事件集合。 应当用程序中调用该函数后,自动将套接字设置为非阻塞模式。 通常,应用程序声明的消息要比Windows的WM_USER值大,以避免该消息与Windows预定消息发生混淆。,注册哪种网络事件,取决于实际的需要。如果应用程序同时对多个网络事件感兴趣。需要对网络事件类型执行按位OR(或)运算。然后将它们分配给lEvent参数。 例如,应用程序希望在套接字上接收有关连接完成、数据可读和套接字关闭的网络事件。那么在应用程序中,调用WSAAsyncSelect()函数如下所示: WSAAsyncSelect(s, hWnd,WM_SOCKET, FD_CONNECT|FD_READ|FD_CLOSE);,当该套接字连接完成、有数据可读或者套接字关闭的网络事件事件发生时,就会有WM_SOCKET消息发送给窗口句柄为hWnd窗口。,窗口例程,当调用WSAAsyncselect()函数后,应用程序会在hWnd窗口例程,以消息形式接收网络事件通知。窗口例程是回调函数,当成功创建窗口后由系统调用。窗口例程声明如下: LRESULT CALLBACK WindowProc( HWND hWnd, /窗口句柄 UINT uMsg, /消息 WPARAM wParam, /消息参数 LPARAM lParam, /消息参数 );,hWnd:窗口句柄。 uMsg消息。对Windows Sockets应用程序来说感兴趣的是在 WSAAsyncSelect()函数中,由应用程序定义的消息。 wParam:消息参数。在 Windows Sockets应用程序中,该参数指明发生网络事件的套接字。 lParam: 消息参数。在 Windows sockets 应用程序中,该参数低字节指明已经发生的网络事件。高字节包含可能出现的错误代码。,在Windows sockets 应用程序中,当WindowProc()函数接收到网络事件消息时,在该函数内执行下面步骤 : (1) 读取lParam参数高字节,判断是否发生了一个网络错误事件。可以使用 WSAGETSELECTERROR宏。 (2) 如果应用程序发现套接字上没有发生任何错误,则读取Iparam低字节,检查到底是发生了什么网络事件。可以使用WSAGETSELECTEVENT宏。,WSAGETSELECTERROR和WSAGETSELECTEVENT宏声明如下: #define WSAGETSELECTEVENT(lParam) LOWORD(lParam) #define WSAGETSELECTERROR(lParam) HIWORD(lParam),WSAAsyncSelect模型示例程序,下面讲解一个服务器程序。该程序是Win32 Application。在该程序中使用WSAAsyncSelect模型管理接受的客户端套接字。该程序是示例程序,忽略主许多细节。程序设计如图所示,按照下面步骤编码。,1. 声明自定义消息。在程序中声明自定义消息WM_SOCKET。 2. 声明窗口例程。 3. 调用MyRegisterClass()函数注册窗口类。 4. 调用InitInstance()函数创建并显示窗口。因为WSAAsyncSelect()函数的第一个参数是窗口句柄,所以要在调用该函数之前创建窗口。 5. 初始化套接字动态库,创建套接字。 6. 调用WSAAsyncSelect()函数注册感兴趣网络事件。该示例程序中,服务器监听套接字,感兴趣的网络事件有FD_ACCEPT和FD_CLOSE. 7. 绑定套接字,开始监听。 8. 消息循环。 9. 释放套接字和申请的其他资源。,1.声明自定义消息,在应用程序中,通常要声明一个比WM_USER值要大的自定义消息,以免与Windows定义的消息冲突。除了声明自定义消息外,在示例程序中还要声明最大字符串长度、服务器端口、数据缓冲区长度。 #define MAX_LOADSTRING 100 /最大字符串长度 #define WM_SOCKET WM_USER+1 /套接字消息 #define PORT 5150 /服务器端口 #define MAX_SIZE_BUF 1024 /数据缓冲区长度,2.声明窗口例程,窗口例程是由Windows系统调用的函数,通常将该函数的定义放在主函数之后,将声明放在主函数之前。在示例程序中为了使主程序结构清晰,将注册窗口类、创建和显示窗口的过程都设计为函数,并提前声明。声明HandleSocketMsg()函数用于对Windows网络事件消息进行处理。 ATOM MyRegisterClass(HINSTANCE hInstance); /注册窗口 BOOL InitInstance(HINSTANCE,int); /初始化实例 /窗口例程 LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); /处理WM_SOCKET消息 void HandleSocketMsg(WPARM wParam,LPARAM lParam);,3.注册窗口类,调用MyRegisterClass()函数注册窗口类。,4.创建和显示窗口,调用InitInstance()函数创建、显示窗口。此时,窗口例程开始接收Windows消息。,5.创建套接字,调用WSAStartup()函数初始化套接字动态库,调用socket()函数创建套接字。,6.注册感兴趣的网络事件,以窗口名柄hWnd和WM_SOCKET为第2、第3个参数调用WSAAsyncSelect()函数。同时注册FD_ACCRPT和FD_CLOSE网络事件。请求系统当FD_ACCEPT和FD_CLOSE网络事件发生时,给hWnd窗口发送WM_SOCKET消息。,WSAAsyncSelect(sListen, hWnd,WM_SOCKET, FD_READ|FD_CLOSE);,7.绑定套接字,调用bind()函数绑定套接字。,8.开始监听,调用listen()函数套接字开始监听。,9.消息循环,在while循环语句中,GetMessage()函数不断从线程消息队列中取出消息。当FD_ACCEPT或者FD_CLOSE网络事件发生时,WM_SOCKET消息被投递到线程消息队列中,GetMessage()函数负责将该消息从线程消息队列中取出,DispatchMessage()函数再将消息发送到窗口例程。,10.程序退出,当GetMessage() 函数接收到WM_QUIT消息时,while循环结束,释放资源,程序退出。,11.窗口例程,当创建窗口成功后WndProc()窗口例程便开始接收Windows消息。在该函数中需要处理许多消息。 例如,当关闭窗口时发送WM_DESTROY消息,在窗口例程中调用PostQuitMessage()函数向线程消息队列投递WM_QUITI消息,GetMessage()函数接收到该消息后,程序退出。,应用程序不感兴趣的消息交给DefWindowProc()函数处理。 当FD_ACCEPT或者FD_CLOSE网络事件发生时,窗口例程接收到WM_SOCKET消息。在窗口例程中调用HandleSocketMsg()函数对触发WM_SOCKET消息的网络事件进行处理。,/窗口例程 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg,WPARAM wParam, LPARAM lParam) switch(message) case WM_SOCKET: /网络事件发生时发送给该窗口的消息 HandleSocketMsg(wParam,lParam); /处理该消息 break; case WM_PAINT: /画客户区 / break; case WM_DESTROY: PostQuitMessage(0); break; /消息处理 default: return DefWindowProc(hWnd,message,wParam,lParam); return 0; ,12.CClient类,在程序中声明Cclient类管理服务器接受客户端的新建套接字。该类构造函数的参数为服务器接受客户端的新建套接字。在析构函数中将套接字关闭。 在该类中声明RecvData()函数接收数据,SendData()函数发送数据,GetSocket()函数返回套接字。,#define MAX_SIZE_BUF 1024 /数据缓冲区长度 class CClient public: CClient(SOCKET s); /构造函数 virtual CClient( ); /析构函数 public: void RecvData(void); /接收数据 void SendData(void); /发送数据 SOCKET GetSocket(void); /获取套接字 private: SOCKET m_s; /套接字 char m_recvBufMAX_SIZE_BUF; /接收数据缓冲区 char m_sendBufMAX_SIZE_BUF; /发送数据缓冲区 ;,13.管理客户端套接字的链表,声明_socktnode结构体。该结构体pClient字段为CClient类指针。pNext变量为指向下一个节点指针。 typedef struct _socktnode CClient *pClient; /CClient类指针 _socktnode *pNext; /指向下一个节点 SOCKETNODE,*PSOCKETNODE;,当服务器接受一个客户端连接请求后,创建一个CClient实例,新建一个SOCKETNODE节点。将实例指针赋值给SOCKETNODE结构体的pClient变量。 为了对链表进行操作,声明如下函数。 AddNode()函数,添加节点。 DeleteNode()函数:删除节点。 GetClient()函数:获得Cclient类指针。 DeleteAllNode()删除所有节点。,14.网络事件消息处理函数,在HandleSocketMsg()函数中调用 WSAGETSELECTERROR宏检查是否有网络错误事件发生。 如果有网络错误事件发生则调用 DeleteNode()函数将该套接字从链表中删除。在前面的讲解中,已经知道 wParam 参数为发生网络事件的套接字。所以,以wParam为参数调用 DeleteNode函数。 如果没有网络错误事件发生,则调用WSAGETSELECTEVENT宏,检查发生了什么网络事件。,如果网络事件为FD_ACCEPT,那么说明此时客户端等待服务器接受连接请求。发生这个网络事件的套接字一定是服务器监听套接字。 调用accept()函数接受客户端连接请求,将该套接字加入链表中,然后以该新建套接字作为参数调用WSAAsyncSelect()函数,为该套接字请求FD_READ、FD_WRITE和FD_CLOSE网络事件。,当HandleSocketMsg()函数接收到FD_READ网络事件时,说明此时在服务器接受的客户端套接字中,某个套接字上存在可读的数据。这个套接字就是wParam参数值。调用GetClient()函数得到保存该套接字的CClient类指针。调用该类的ecvData()函数接收客户端数据。 HandleSocketMsg()函数接收到FD_WRITE网络事件时的处理方法,同收到FD_READ网络事件时的处理方法相似。 当该函数接收到FD_CLOSE网络事件时,说明此时客户端关闭了套接字,可以调用DeleteNode()函数删除该客户端节点。,/WM_SOCKET消息处理 void HandleSocketMsg(WPARAM wParam,LPARAM lParam) if (WASGETSELECTERROR(lParam) /检查网络错误 DeleteNode(wParam); /删除节点,关闭套接字 else /检查网络事件 switch(WASGETSELECTEVENT(lParam) case FD_ACCEPT: /接受客户端连接请求, SOCKET sAccept; if(sAccept=accept(wParam,NULL,NULL) =INVALID_SOCKET) break; AddNode(sAccept); /将套接字加入链表中 /FD_READ、FD_WRITE、FD_CLOSE事件发生 /时,发送WM_SOCKET消息 WSAAsyncSelect(sAccept,hWnd,WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE); break; ,case FD_READ: /接收数据 CClient* pClient=GetClient(wParam);/根据套接字, /获取客户端节点 pClient-RecvData(); /接收数据 break; case FD_WRITE: CClient* pClient=GetClient(wParam);/根据套接字, /获取客户端节点 pClient-SendData(); /发送数据 break; ,case FD_CLOSE: /对方关闭了套接字连接 DeleteNode(wParam); /删除节点, break; return; ,调用WSAAsyncSelect()函数注意问题,接收不到网络事件 第一种情况是由于在同一个套接字同一个自定义消息上,多次调用WSAAsyncSelect()函数注册不同的网络事件,最后一次函数调用取消了前面注册的网络事件。 例如,在应用程序中,第一次调用WSAAsyncSelect()函数注册FD_READ网络事件,然后又调用该函数注册F_WRITE 网络事件,那么此时应用程序,就只能接收到FD_WRITE网络事件。,如果要取消所有请求的网络事件通知,告知indows Sockets实现不再为该套接字发送任何网络事件相关的消息,要以参数IEvent值为0调用WSAAsyncSelect()函数。 WSAAsyncSelect(s,hWnd,0,0); 需要注意尽管应用程序调用上述函数取消了网络事件通知,但是在应用程序消息队列中,可能还有网络消息在排队。所以在调用WSAAsyncSelect()函数取消网络事件消息后,应用程序还应该继续准备接收网络事件。,第二种情况是在同一个套接字上,多次调用WSAAsyncSelect()函数,为不同的网络事件定义了不同的消息,最后一次该函数调用将取消前面注册的网络事件。 下面的代码中,第二次函数调用将会取消第一次函数调用的作用。只有FD_WRITE网络事件能过wMsg2消息通知到窗口。 WSAAsyncSelect(s,hWnd,wMsg1,FD_READ); WSAAsyncSelect(s,hWnd,wMsg2,FD_WRITE);,关于accept()函数,因为调用accept()函数接受的套接字和监听套接字具有同样的属性。所以,任何为监听套接字设置的网络事件对接受的套接字同样起作用。 如果一个监听套接字请求FD_ACCEPT、FD_READ和FD_WRITE网络事件,则在该监听套接字上接受的任何套接字也会请求FD_ACCEPT、FD_READ和FD_WRITE网络事件,以及发送同样的消息。,若需要不同的消息和网络事件,应用程序应该调用WSAAsyncSelect()函数,为该套接字请求不同的网络事件和消息。,关于FD_READ网络事件,为一个FD_READ网络事件不要多次调用recv()函数。如果应用程序为一个FD_READ网络事件,调用了多个recv()函数,会使得该应用程序接收到多个_READ网络事件。 如果在一次接收FD_READ网络事件时需要调用多次recv()函数,应用程序应该在调用recv()函数之前关闭FD_READ 消息。 应用程序不必在收到FD_READ消息时,读进所有可读的数据。每接收到一次FD_READ网络事件,应用程序调用一次recv()函数是恰当的。,如何判断套接字已经关闭,要使用FD_CLOSE网络事件来判断套接字是否已经关闭。 接收FD_CLOSE网络事件时,错误代码指示出套接字是从容关闭还是硬关闭。如果错误代码0,则为从容关闭;若错误代码为WSAECONNRESET,则套接字是硬关闭。 如果套接字从容关闭,数据已经都全部接收,应用程序只会收到FD_CLOSE消息来指出虚电路关闭,它不会收到FD_READ消息来表明这种状况。 调用closesocket()函数后不会投递FD_CLOSE网络事件。,发送数据失败,一个应用程序当接收到第一个FD_WRITE网络事件后,便认为在该套接字上可以发送数据。当调用输出函数发送数据时,会收到WSAEWOULDBLOCKE错误。经过这样的失败后,要在下一次接收到FD_WRITE 网络事件后,再次发送数据,才能够将数据发送出去。,发生网络事件的条件,调用WSAAsyncSelect()函数只是请求系统在网络事件发生时,给窗口发送消息.开发人员需要了解在什么情况下会发生网络事件下。下面对发生网络事件条件进行小结。,FD_READ网络事件,在下面情况下,发生FD_READ网络事件。 当调用WSAAsyncSelect()函数时,如果当前有可读数据时。 当数据到达并且没有发送FD_READ网络事件时。 调用recv()或者recvfrom()函数后,如果仍然有可读数据时。,FD_WRITE事件,在下面情况下,发生FD_WRITE网络事件。 当调用WSAAsyncSelect()函数时,如果调用能够发送数据时。 调用connect()或者accept()函数后,当连接已经建立时。 调用send()或者sendto()函数,返回WSAEWOULDBLOCKE错误后,再次调用send()或者sendto()函数可能成功时。,FD_ACCEPT事件,在下面情况下,发生FD_ACCEPT网络事件。 当调用WSAAsyncSelect()函数时,如果当前有连接请求需要接受时。 当连接请求到达,还没有发送FD_ACCEPT网络事件时。 调用accept()函数后,如果还有另外连接请求需要接受时。,FD_CONNECT事件,在下面情况下,发生FD_CONNECT网络事件。 当调用WSAAsyncSelect()函数,如果当前一个连接已经建立时。 当调用connect()函数后,建立连接完成时。 当调用WSAJoinLeaf()函数后,加入操作完成时。 在面向连接的非阻塞套接字上,调用connect()、WSAConnect()或者WSAJoinLeaf()函数后,尝试连接完成时。此时应用程序应该检查错误代码,确定连接是否成功。,FD_CLOSE事件,FD_CLOSE事件仅对面向连接套接字有效,在下面情况下发送FD_CLOSE事件。 当调用WSAAsyncSelect()函数时,套接字连接关闭时。 对方执行了从容关闭后,没有数据可读时。如果数据已经到达并等待读取,FD_CLOSE事件不会被发送,直到所有的数据都被接收。 调用shutdown()函数执行从容关闭,对方应答FIN后,此时如果没有数据可读时。 当对方结束了连接,并且IParam包含WSAECONNRESET错误时。,WSAAsyncSelect模型优势和不足,1.优势 该模型的使用方便了基于消息的Windows环境下开发
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年茶艺师职业技能鉴定理论试卷(茶艺师职业培训师资培训报告)
- 2025年电梯安装改造维修作业特种作业操作证考试试卷(电梯安全)事故案例分析
- 低碳城市案例研究:2025年城市规划与城市更新策略
- 帕力亚多骆驼乳粉知识测试题
- 2025年基因治疗药物临床研发新趋势:市场前景与产业布局分析报告
- 2025年高性能铁氧体一次磁粉项目规划申请报告
- 汽车工程原理及技术案例分析题
- 2025年防雷工程项目立项申请报告
- 金融大数据在反欺诈中的机器学习应用报告2025
- 2025年社交媒体平台文化影响力报告:热点事件与舆论引导效应
- 中药饮片标签管理规定
- 集装箱堆场事故应急预案方案
- 计算机网络与信息安全-习题参考答案
- 2024年瓦斯检查工技能竞赛理论考试题库500题(含答案)
- 2024年宁夏中考数学真题含解析
- 企业信息化建设与管理咨询合同
- 部编版六年级下册道德与法治全册教案教学设计
- 国家开放大学电大《基础写作》期末题库及答案
- 2025年中考作文试题预测及范文
- 河南省郑州2023-2024学年八年级下学期期末模拟-英语试卷(含解析)
- 法院婚内财产协议书模板
评论
0/150
提交评论