收藏 分销(赏)

网络WSAAsyncSelect网络模型讲解.ppt

上传人:pc****0 文档编号:13174937 上传时间:2026-01-29 格式:PPT 页数:64 大小:813KB 下载积分:10 金币
下载 相关 举报
网络WSAAsyncSelect网络模型讲解.ppt_第1页
第1页 / 共64页
网络WSAAsyncSelect网络模型讲解.ppt_第2页
第2页 / 共64页


点击查看更多>>
资源描述
,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,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_LOADSTRING100 /,最大字符串长度,#define WM_SOCKETWM_USER+1/,套接字消息,#define PORT5150/,服务器端口,#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_BUF1024/,数据缓冲区长度,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,环境下开发套接字应用程序。开发人员可以像处理其他消息一样对网络事件消息进行处理。,该模型为确保接收所有数据提供很好的机制。通过注册,FD_CLOSE,网络事件,从容关闭服务器与客户端的连接保证了数据全部接收。,2.,不足,该模型局限在于,它基于,Windows,的消息机制,必须在应用程序中创建窗口。当然,在开发中可以根据具体情况确定是否显示该窗口,,MFC,的,CSockeWnd,类就是用来创建一个不显示的窗口,并在该类中声明接收网络事件消息处理数。,由于调用,WSAAsnycSelect,(),函数后,自动将套接字设置为非阻塞状态。当应用程序为接收到网络事件调用相应的函数时,未必能够成功返回。这无疑增加了开发人员使用该模型的难度。对于这一点可以从,MFC,CSocket,类的,Accpet,(),、,Receive(),和,Send(),函数的实现中得到验证。,局域网简易聊天程序,需求分析,该聊天程序分为服务器和客户端两个部分。,客户端实现以下功能。,向在线成员发送消息并接收消息。,更新成员状态。当其他成员上线或者下线时,客户端及时更新该成员状态。,更新成员列表。当新成员加入或者成员被删除时,客户端及时更新成员列表。,当对方不在线时,可以向该成员发送离线消息。,当用户上线时,接收其他用户发送的离线消息,保存聊天记录。,服务器实现以下功能,维护聊天室成员列表。包括接受新成员,删除已有成员。,通知客户端更新成员状态,当用户上线或者下线时,通知客户端更新成员状态。,通知客户端更新成员列表。当有新用户加入或者成员被删除时,通知客户端列新成员列表,为离线客户端保存聊天消息。,验证密码。当用户登录时,验证密码。,禁止已经登录用户,再次登录。,保存用户信息。,为用户保存离线消息。,客户端启动时登录服务器,如果用户为非注册用户则服务器为该用户注册信息。注册信息包括该用户的名称和密码等。以后该用户需要使用注册名称密码登录服务器。服务器以用户名称作为每个用户的唯一标识。用户注册后,服务器向该用户发送用户列表。,注册用户登录服务器时,如果输入密码正确,则服务器请向该用户发送用户列表。如果服务器验证该用户输入密码错误,则通知该用户重新登录。,当某个成员上线或者下线时,服务器通知在线成员更新该用户状态。,当某个成员希望与另外一成员交流时,如果另外那个成员在线则可以其送消息并接收应答。,当某个成员希望与另外一成员交流时,如果另外那个成员不在线则可以向发送离线消息。当对方上线时将自动接收到离线消息。,
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 百科休闲 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服