网络软件设计——异步响应.pptx
1消息驱动与Socket服务机制的矛盾阻塞?造成主线程无法响应窗口消息非阻塞?设置套接字为非阻塞状态(见课件9)主线程忙等无法响应窗口消息select?主线程受select机制影响仍无法响应窗口消息多线程并发服务?子线程可以独立工作,不致影响主线程响应消息但主线程一旦需要循环等待接受客户连接,仍出现以上问题基于基于windowswindows消息的循环消息的循环基于基于selectselect的循环的循环第1页/共21页2矛盾的根源使用套接字时的循环机制循环等待客户连接循环等待客户数据在窗口程序中循环或阻塞都会影响主线程对消息的响应while(1)while(1)ns=accept(sock,);ns=accept(sock,);while(recv 0)while(recv 0)len=recv(sock,);len=recv(sock,);事件事件第2页/共21页3解决矛盾方案一为所有需要循环等待的程序段生成线程为需要循环等待客户连接的部分生成子线程为需要循环等待数据、完成通信的部分生成子线程窗口函数窗口函数clickclickstartstartwhile(1)while(1)accept();accept();while(1)while(1)recv();recv();客户客户connectconnect客户客户connectconnectwhile(1)while(1)recv();recv();第3页/共21页4解决矛盾方案二不在程序中循环等待如何促使程序不断接受客户连接,接收数据?窗口函数窗口函数clickclickstartstart select();select();while(1)while(1)窗口程序是基于消息的,如果将客户连接、数据到达等也映射为消息则通信程序窗口程序是基于消息的,如果将客户连接、数据到达等也映射为消息则通信程序也可以基于消息驱动,不必自己去循环等待也可以基于消息驱动,不必自己去循环等待异步响应异步响应WSAAsyncSelect()WSAAsyncSelect()第4页/共21页5WSAAsyncSelect()异步选择机制使用WSAAsyncSelect()注册网络事件当套接字上出现出现相应的事件时,会用消息通知窗口函数处理接受连接接收数据连接被关闭WSAAsyncSelect()参数设计套接字窗口句柄消息,事件第5页/共21页6WSAAsyncSelect()函数说明int WSAAsyncSelect(SOCKET s,int WSAAsyncSelect(SOCKET s,HWND hWnd,HWND hWnd,unsigned int wMsg,unsigned int wMsg,long lEvent)long lEvent)wMsgwMsg:套接字消息,如:套接字消息,如UM_SOCKUM_SOCK,该消息通过以下方式定义,该消息通过以下方式定义 define UM_SOCK WM_USERdefine UM_SOCK WM_USER1 1lEventlEvent:套接字事件,事件类型有:套接字事件,事件类型有FD_READFD_READFD_WRITEFD_WRITEFD_ACCEPTFD_ACCEPTFD_CONNECTFD_CONNECTFD_CLOSEFD_CLOSEFD_OOBFD_OOB有数据接收有数据接收可以发送可以发送有客户建立连接有客户建立连接与服务器建立连接,或连接失败与服务器建立连接,或连接失败connetconnet的结果的结果连接被关闭连接被关闭带外(紧急)数据到达带外(紧急)数据到达第6页/共21页7WSAAsyncSelect()函数功能及使用方法说明该函数用于将指定的套接字上发生的指定消息及事件向指定窗口注册当套接字上发生指定事件时,系统会通过消息机制通知指定的窗口函数处理int WSAAsyncSelect(SOCKET s,int WSAAsyncSelect(SOCKET s,HWND hWnd,HWND hWnd,unsigned int wMsg,unsigned int wMsg,long lEvent)long lEvent)指定套接字指定套接字指定消息指定消息指定事件指定事件指定窗口指定窗口WinMainWinMainWndProcWndProcWndProcWndProcWndProcWndProcDispatchMessage()DispatchMessage()第7页/共21页8WSAAsyncSelect()函数功能及使用方法说明该函数用于将指定的套接字上的指定消息及事件向指定窗口注册当套接字上发生指定事件时,系统会通过消息机制通知指定的窗口函数处理换言之,如果不事先注册,套接字上的事件不会通知窗口函数注册多个事件时,使用以下方式WSAASyncSelect(s,hWnd,UM_SOCK,FD_READ|FD_ACCEPT|FD_CLOSE);当窗口收到套接字消息后,message为UM_SOCK,事件用lParam通知,发生事件的套接字标识符由wParam通知使用了WSAAsyncSelect的套接字自动变成非阻塞状态select不具有这个功能可以自己定义可以自己定义系统定义系统定义第8页/共21页9WSAAsyncSelect()函数功能及使用方法说明与select不同的是,注册只需进行一次,系统会一直保持对事件的敏感,直到重新注册第二次注册时,会取消上次注册的消息例:下面两段程序并不等价WSAAsyncSelect(s,hWnd,UM_SOCK,FD_READ|FD_WRITE);WSAAsyncSelect(s,hWnd,UM_SOCK,FD_READ|FD_WRITE);希望系统对希望系统对s s上的上的FD_READFD_READ和和FD_WRITEFD_WRITE事件进行通知事件进行通知WSAAsyncSelect(s,hWnd,UM_SOCK,FD_READ);WSAAsyncSelect(s,hWnd,UM_SOCK,FD_READ);WSAAsyncSelect(s,hWnd,UM_SOCK,FD_WRITE);WSAAsyncSelect(s,hWnd,UM_SOCK,FD_WRITE);系统仅对系统仅对s s上的上的FD_WRITEFD_WRITE事件进行通知事件进行通知第9页/共21页10异步选择机制对消息/事件的注册用于实现不同功能的套接字,可能注册不同的事件对事件的响应事件使能(重新允许)更多的错误处理第10页/共21页11异步选择机制对消息/事件的注册服务器主套接字上注册FD_ACCEPT事件从套接字上注册FD_READ|FD_CLOSE等事件客户机套接字注册FD_CONNECT事件对事件的响应判断为UM_SOCK消息后,进一步获得套接字标识符和事件根据事件,在套接字上调用相应函数FD_ACCEPTaccept(s)FD_READ recv(s),read(s)FD_CONNECT连接已建立,客户可以发出数据了FD_CLOSEclosesocket(s)FD_WRITE send(s),write(s)事件使能(重新允许)Windows对用户产生事件后,如果用户没有对事件处理,Windows将不对用户继续产生事件。必须针对每个事件进行处理,以使系统继续产生新的事件第11页/共21页12例服务器程序(与前一部分的程序相比)在StartServer()中使用WSAAsyncSelect而不是用循环的方式等待accept在窗口函数中添加UM_SOCK类消息,并进一步添加对各事件的处理bind(mainsock,);bind(mainsock,);listen(mainsock,);listen(mainsock,);WSAAsyncSelect(mainsock,hWnd,UMSOCK,FD_ACCEPT)WSAAsyncSelect(mainsock,hWnd,UMSOCK,FD_ACCEPT)第12页/共21页13例套接字消息处理WndProc()WndProc()switch(message)switch(message)case UM_SOCK:case UM_SOCK:id=LOWORD(lParam);id=LOWORD(lParam);sock=wParam;sock=wParam;switch switch(idid)case FD_ACCEPT case FD_ACCEPT:ns ns acceptaccept(socksock,););WSAAsyncSelect(ns,WSAAsyncSelect(ns,FD_READ|FD_CLOSE);FD_READ|FD_CLOSE);case FD_READ:case FD_READ:recv recv(socksock,buf);buf);第13页/共21页14例客户机程序在start中,调用connect,但要注意,connect返回时,并不意味着连接已建立,需要等待FD_CONNECT事件在窗口函数中增加套接字事件处理sock sock socketsocket();WSAAsyncSelectWSAAsyncSelect(socksock,,FD_CONNECT);,FD_CONNECT);填写填写serverserver的的IPIP地址和端口号;地址和端口号;connect(sock,server,);connect(sock,server,);第14页/共21页15例套接字消息处理WndProc()WndProc()switch(message)switch(message)case UM_SOCK:case UM_SOCK:event=LOWORD(lParam);event=LOWORD(lParam);sock=wParam;sock=wParam;switch switch(eventevent)case FD_CONNECT:case FD_CONNECT:if(if(HIWORD(lParam)HIWORD(lParam)=0)=0)WSAAsyncSelect(sock,UM_SOCK,FD_READ|FD_CLOSE);WSAAsyncSelect(sock,UM_SOCK,FD_READ|FD_CLOSE);elseelseclosesocket(sock);closesocket(sock);break;break;case FD_READ:case FD_READ:recv recv(socksock,buf);buf);第15页/共21页16异步选择机制事件使能(重新允许)实验:在出现FD_READ事件后,没有调用recv(),观察系统是否再次产生FD_READ事件。对比实验:出现FD_READ事件后,调用recv()接收,不管是否接收完,系统都会根据需要继续产生FD_READ事件第16页/共21页17异步选择机制WinSock下具有重新允许功能的函数,及与相应事件的对应关系FD_READ recv()或recvfrom()FD_WRITE send()sendto()FD_ACCEPT accept()FD_CONNECT NONEFD_CLOSE NONEFD_OOB recv()第17页/共21页18异步选择机制更多的错误处理原因基于消息的程序机制,具有多个入口(菜单、按钮、选项)使用者可能任意选择入口,对程序流程造成意想不到的破坏,如还未建立连接,就点击发送键方法一迫使用户按照程序设计者规定的流程。在每个阶段,将不应该进入的菜单、按钮、选项失效(变灰)方法二进行更多的错误状态判断,使每个入口的执行条件更为严格。第18页/共21页19异步选择与多路复用Windows异步选择机制中,在通知用户的消息中,也同时通知了产生事件的套接字描述符,所以有以下结论:异步选择机制支持多路服用。即可以对多个套接字同时进行事件注册,并同时处理与select相比,程序员自行管理套接字队列的功能不是必需的。winsock下的多路复用由系统支持同时也受到系统的限制对于大型、复杂的服务器程序,程序员仍应根据需要考虑对套接字队列的管理。甚至,慎重考虑是否需要建立在windows的窗口机制下。第19页/共21页20同步选择与异步选择的比较基于select的同步选择不断查询程序框架是不断循环同步性:查询结果是套接字当时状态需要用户自行管理多个套接字以备查询不存在事件使能,即如果不处理查询得到的事件,下次查询,该事件仍然存在n nWSAAsyncSelectWSAAsyncSelect异步选择异步选择l l一次注册一次注册l l程序尽量不出现循环程序尽量不出现循环l l异步性:不保证通知用户后,用户能及时异步性:不保证通知用户后,用户能及时了解和处理了解和处理l l用户不必管理套接字,事件通知中包含套用户不必管理套接字,事件通知中包含套接字标识符接字标识符l l存在事件使能,如果不处理通知的事件,存在事件使能,如果不处理通知的事件,后续事件将不再通知,直到完成相应处理,后续事件将不再通知,直到完成相应处理,即事件使能即事件使能都支持多路服用都支持多路服用都可以看作是事件驱动,只不过一个是查询事件,一个是都可以看作是事件驱动,只不过一个是查询事件,一个是等待等待“事件通知事件通知”第20页/共21页21感谢您的观看!第21页/共21页