《第三次课 windows套接字IO模型》由会员分享,可在线阅读,更多相关《第三次课 windows套接字IO模型(27页珍藏版)》请在金锄头文库上搜索。
1、复习复习v1 IP特点特点v2 TCP与与UDP区别区别v3 基于基于TCP连接的两个程序的通信过程连接的两个程序的通信过程v4 基于基于UDP的两个程序的通信过程的两个程序的通信过程1IO模型的模型的6种形式种形式v阻塞阻塞v选择选择vWSAAsyncSelectvWSAEventSelectv重叠重叠overlappedv完成端口完成端口completion port2阻塞模式阻塞模式vRecv函数的调用会使程序进入等待状态,直函数的调用会使程序进入等待状态,直到接收到数据才返回。到接收到数据才返回。Accept函数的调用也函数的调用也进入等待状态,直到有连接才返回进入等待状态,直到有连接
2、才返回v此模式容易理解,但如果有多个套接字连接此模式容易理解,但如果有多个套接字连接时,就必须创建多个线程,一个连接使用一时,就必须创建多个线程,一个连接使用一个线程。因此实际开发中使用最多的还是非个线程。因此实际开发中使用最多的还是非阻塞模式阻塞模式3vSOCKET sServSock; sockaddr_in addr; int nSockErr,nNumConns;vSOCKET sConns5; sockaddr Connaddrs5;vint nAddrLen=sizeof(sockaddr);vsServSock=socket(AF_INET,SOCK_STREAM,0); /建立
3、建立socket对象对象vaddr.sin_family=AF_INET;vaddr.sin_port=htons(5050); /为为socket分配端口分配端口vaddr.sin_addr.s_addr=htonl(INADDR_ANY);vif(bind(sServSock,(LPSOCKADDR)&addr,sizeof(addr)=SOCKET_ERROR) v nSockErr=WSAGetLastError()();vreturn; /Handle error,Do not continuevvif(listen(sServSock,2)= =SOCKET_ERROR)/监听客户
4、连接请求监听客户连接请求v nSockErr=WSAGetLastError()();v return;/处理错误,不再继续处理错误,不再继续v4vListen发生错误一般由于绑定没有成功引起发生错误一般由于绑定没有成功引起vShutdown与与closesocket的区别的区别5vwhile(nNumconns0) /发生读事件发生读事件v /对于这个简单的例子,对于这个简单的例子,select()应该返回值()应该返回值1。v /处理多个套接字的应用程序应该返回比处理多个套接字的应用程序应该返回比1大的值。大的值。v if(FD_SET(s,&fdread)v /套接字套接字s上发生读事件
5、上发生读事件v v vv若想要在这个应用程序中添加更多的套接字,只需为额外的套接字维护它们的一个列表。若想要在这个应用程序中添加更多的套接字,只需为额外的套接字维护它们的一个列表。v源代码示例源代码示例15WSAAsyncSelect模型模型vWSAAsynSelect是是Winsock提供的一个常用的提供的一个常用的I/O模型。利用这个模型,模型。利用这个模型,应用程序可以在一个套接字上,接收以应用程序可以在一个套接字上,接收以Windows消息为基础的网络事件消息为基础的网络事件通知。该模型的实现方法是通过调用通知。该模型的实现方法是通过调用WSAAsynSelect函数自动将套接函数自动
6、将套接字设置为非阻塞模式,并向字设置为非阻塞模式,并向Winsock DLL注册一个或多个感兴趣的网络注册一个或多个感兴趣的网络事件,并提供一个通知时使用的窗口句柄,当注册的网络事件发生时,事件,并提供一个通知时使用的窗口句柄,当注册的网络事件发生时,对应的窗口将收到一个基于消息的通知。对应的窗口将收到一个基于消息的通知。v 要想使用要想使用WSAAsyncSelect模型,在应用程序中,首先必须用模型,在应用程序中,首先必须用CreateWindow函数创建一个窗口,再为该窗口提供一个窗口例程支持函数创建一个窗口,再为该窗口提供一个窗口例程支持函数(函数(Winproc)。也可以使用一个对话
7、框,为其提供一个对话例程,)。也可以使用一个对话框,为其提供一个对话例程,因为本质上将,对话框也是窗口。设置好窗口的框架后,便可开始创建因为本质上将,对话框也是窗口。设置好窗口的框架后,便可开始创建套接字,并调用套接字,并调用WSAAsyncSelect函数,打开窗口消息通知。函数,打开窗口消息通知。 16vWSAAsyncSelect函数定义如下:函数定义如下:vint WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);v参数说明:参数说明:vs:指定需要事件通知的套接字的描述符。:指定需要事件通知的套接字的描述
8、符。vhWnd:标识一个在网络事件发生时需要接收消息的窗口句:标识一个在网络事件发生时需要接收消息的窗口句柄。柄。vWMsg:指定在发生网络事件时,打算接收的消息。该消息:指定在发生网络事件时,打算接收的消息。该消息会投递到由会投递到由hWnd窗口句柄指定的那个窗口。窗口句柄指定的那个窗口。vlEvent:指定一个位掩码,对应于一系列事件的组合。:指定一个位掩码,对应于一系列事件的组合。17WSAAsyncSelectWSAAsyncSelect函数网络事件类型函数网络事件类型事件类型事件类型含义含义FD_READFD_READ应用程序想要接收有关是否可用程序想要接收有关是否可读的通知,以便的
9、通知,以便读入数据入数据FD_WRITEFD_WRITE应用程序想要接收有关是否可写的通知,以便用程序想要接收有关是否可写的通知,以便读入数据入数据FD_OOBFD_OOB应用程序想要接收是否有带外(应用程序想要接收是否有带外(OOBOOB)数据抵达的通知数据抵达的通知FD_ACCEPTFD_ACCEPT应用程序想要接收与进入连接有关的通知应用程序想要接收与进入连接有关的通知FD_CONNECTFD_CONNECT应用程序想要接收与一次连接或者多点应用程序想要接收与一次连接或者多点joinjoin操作完成的通知操作完成的通知FD_CLOSEFD_CLOSE应用程序想要接收与套接字关闭有关的通知
10、应用程序想要接收与套接字关闭有关的通知FD_QOSFD_QOS应用程序想要接收套接字应用程序想要接收套接字“服务质量服务质量”(QOSQOS)发生更改的通知发生更改的通知FD_GROUP_QOSFD_GROUP_QOS应应用用程程序序想想要要接接收收套套接接字字组组“服服务务质质量量” 发发生生更更改改的的通通知知(现现在没有用,为将来套接字组的使用保留)在没有用,为将来套接字组的使用保留)FD_ROUTING_INTFD_ROUTING_INTERFACE_CHANGEERFACE_CHANGE应用程序想接收在指定的方向上,与路由接口发生变化的通知应用程序想接收在指定的方向上,与路由接口发生
11、变化的通知FD_ADDRESS_CFD_ADDRESS_CHANGEHANGE应应用用程程序序想想要要接接收收针针对对套套接接字字的的协协议议家家族族,本本地地地地址址列列表表发发生生变化的通知变化的通知用于用于WSAAsyncSelect函数的网络事件类型函数的网络事件类型 18v当被指定的事件发生时,程序接收到消息,当被指定的事件发生时,程序接收到消息,消息的消息的MSG结构体中,结构体中,message项就是被规项就是被规定的消息名称定的消息名称wMsg,lParam的内容就是网的内容就是网络事件名称。络事件名称。19注意注意vWSAAsyscSelect的设定是针对某一个的设定是针对某
12、一个Socket,也,也就是说,只有当设定的就是说,只有当设定的Socket事件发生时,才会收事件发生时,才会收到这个信息。如果开启了很多到这个信息。如果开启了很多Socket,要让每个,要让每个Socket都变成异步模式,那么就必须对每一个都变成异步模式,那么就必须对每一个Socket都呼叫都呼叫WSAAsyscSelect来一一设定。如果来一一设定。如果想将某一个想将某一个Socket的异步事件通知设定取消,那么的异步事件通知设定取消,那么同样也是用同样也是用WSAAsyscSelect函数,第四个参数函数,第四个参数lEvent一定为一定为020v呼叫呼叫WSAAsyscSelect的同
13、时,也将的同时,也将Socket改变成非阻塞模式。但是此时不能简单的嗲改变成非阻塞模式。但是此时不能简单的嗲用用ioctlsocket()将将socket便会阻塞模式,如便会阻塞模式,如果要变回,必须先呼叫果要变回,必须先呼叫WSAAsyscSelect取取消所有异步事件(第四个参数为消所有异步事件(第四个参数为0)然后调用)然后调用ioctlsocket将它变回阻塞模式将它变回阻塞模式21大大多多数数应应用用程程序序感感兴兴趣趣的的网网络络事事件件类类型型包包括括:FD_READFD_READ、FD_WRITEFD_WRITE、FD_ACCEPTFD_ACCEPT、FD_CONNECTFD_
14、CONNECT和和FD_CLOSEFD_CLOSE。如如果果应应用用程程序序同同时时对对多多个个网网络络事事件件有有兴兴趣趣,需需对对各各种种类类型型执执行行一一次次简简单单的的按按位位OROR(或或)运运算算,然然后后将将它们分配给它们分配给lEventlEvent。举例来说:举例来说:WSAAsyncSelect(s,hWnd,WM_SOCKETWSAAsyncSelect(s,hWnd,WM_SOCKET, FD_READ| FD_WRITE| FD_CLOSE), FD_READ| FD_WRITE| FD_CLOSE)调调用用这这个个函函数数,应应用用程程序序就就可可以以在在套套接接
15、字字s s上上,接接收收到到有有关关发发送送、接接收收以以及及套套接接字字关关闭闭等等网网络络事事件件的的通通知知。特特别别要要注注意意的的是是,多多个个事事件件必必须须在在套套接接字字上上一一次次注注册册。一一旦旦某某个个套套接接字字上上允允许许了了事事件件通通知知,那那么么这这个个事事件件通通知知一一直直有有效效,除除非非以以后后明明确确调调用用closesocketclosesocket命命令令,或或者者由由应应用用程序再次调用程序再次调用WSAAsyncSelectWSAAsyncSelect,更改注册网络事件类型。更改注册网络事件类型。若应用程序调用了若应用程序调用了WSAAsync
16、Select,那么套接字的模式从那么套接字的模式从“阻塞阻塞”自动变成自动变成“非阻塞非阻塞”。应用程序在一个套接字上成功调用了。应用程序在一个套接字上成功调用了WSAAsyncSelect之后,应用程序会在之后,应用程序会在hWnd窗口句柄参数对应的例程窗口句柄参数对应的例程中,以中,以Windows消息的形式,接收网络事件通知。消息的形式,接收网络事件通知。 22窗口例程通常定义如下:窗口例程通常定义如下:LRESULT LRESULT CALLBACK CALLBACK WindowProc(HWNDWindowProc(HWND hWndhWnd, , UNIT UNIT uMsguM
17、sg, , WPARAM WPARAM wParamwParam, , LPARAM LPARAM lParamlParam););参数说明:参数说明:hWndhWnd:窗口句柄,对窗口例程的调用正是由那个窗口发出的。窗口句柄,对窗口例程的调用正是由那个窗口发出的。uMsguMsg:指指定定需需要要对对那那些些消消息息进进行行处处理理。对对WSAAsyncSelectWSAAsyncSelect模模型型来来说说,感感兴兴趣趣的的是是WSAAsyncSelectWSAAsyncSelect调用中定义的消息。调用中定义的消息。wparamwparam: :指定在其上发生网络事件的套接字指定在其上发
18、生网络事件的套接字. .lParamlParam: :包包含含了了两两方方面面的的信信息息。低低位位字字指指定定了了已已经经发发生生的的网网络络事事件件,高高位位字字包包含了可能出现的错误代码。含了可能出现的错误代码。 网网络络事事件件消消息息抵抵达达一一个个窗窗口口例例程程后后,应应用用程程序序首首先先应应检检查查lParam的的高高字字位位,判判断断是是否否在在套套接接字字上上发发生生了了一一个个网网络络错错误误。这这里里有有一一个个特特殊殊的的宏宏: WSAGETSELECTERROR,可可用用它它返返回回高高字字位位包包含含的的错错误误信信息息。若若应应用用程程序序发发现现套套接接字字
19、上上没没有有产产生生任任何何错错误误,接接着着便便判判断断发发生生了了哪哪个个网网络络事事件件,具具体体的的做做法法便便是是读读取取lParam之之低低字字位位的的内内容容。此此时时可可使使用用另另一一个个特特殊殊的的宏宏:WSAGETSELECTEVENT,用它返回用它返回lParam的低字部分。的低字部分。23下面的例子演示了如何使用下面的例子演示了如何使用WSAAsyncSelectWSAAsyncSelect这种这种I/OI/O模型,来实现窗口消息的模型,来实现窗口消息的管理。在源程序中,我们着重强调的是开发一个基本服务器应用要涉及到的基管理。在源程序中,我们着重强调的是开发一个基本服
20、务器应用要涉及到的基本步骤,忽略了开发一个完整的本步骤,忽略了开发一个完整的WindowsWindows应用需要涉及到的大量编程细节。应用需要涉及到的大量编程细节。这个个例例子子只只给出出如如何何开开发一一个个简单的的服服务器器程程序序。它它会会监监听听20002000端端口口上上的的连连接接请请求求,并并且且接接受受TCPTCP连连接接。当当这这个个程程序序收收到到来来自自客客户户端端的的/ /数数据据,直直到到客客户户端端关关闭连接。客户端程序使用前面会话套接字例子中的客户端程序。闭连接。客户端程序使用前面会话套接字例子中的客户端程序。 /服务器端程序服务器端程序#include #inc
21、lude #include #include #include #include #include #include # #pragmapragma comment(lib,ws2_32.lib) comment(lib,ws2_32.lib)#define PORT 2000#define PORT 2000#define DATA_BUFSIZE 80#define DATA_BUFSIZE 80#define WM_SOCKET (WM_USER + 1)#define WM_SOCKET (WM_USER + 1) 24void main(void)void main(void) MS
22、G MSG msgmsg; DWORD Ret; SOCKET Listen; DWORD Ret; SOCKET Listen; SOCKADDR_IN SOCKADDR_IN InternetAddrInternetAddr; HWND Window; WSADATA ; HWND Window; WSADATA wsaDatawsaData; ; if (Window = if (Window = MakeWorkerWindowMakeWorkerWindow() = NULL) /() = NULL) /建立窗口建立窗口 return; return; WSAStartup(0x02
23、02, &WSAStartup(0x0202, &wsaDatawsaData) != 0) /) != 0) /初始化套接字初始化套接字 Listen = socket (AF_INET, SOCK_STREAM, 0) /Listen = socket (AF_INET, SOCK_STREAM, 0) /创建套接字创建套接字 /异步选择异步选择 WSAAsyncSelect(ListenWSAAsyncSelect(Listen, Window, WM_SOCKET, FD_ACCEPT|FD_CLOSE);, Window, WM_SOCKET, FD_ACCEPT|FD_CLOSE)
24、; / /本地地址填充本地地址填充 InternetAddr.sin_familyInternetAddr.sin_family = AF_INET; = AF_INET; InternetAddr.sin_addr.s_addrInternetAddr.sin_addr.s_addr = = htonl(INADDR_ANYhtonl(INADDR_ANY);); InternetAddr.sin_portInternetAddr.sin_port = = htons(PORThtons(PORT); ); 25/套接字绑定套接字绑定bind(Listen, (PSOCKADDR) &bin
25、d(Listen, (PSOCKADDR) &InternetAddrInternetAddr, , sizeof(InternetAddrsizeof(InternetAddr) ) listen(Listen, 5) /listen(Listen, 5) /置套接字为监听模式置套接字为监听模式 /为应用程序解释和分发窗口消息为应用程序解释和分发窗口消息while(Ret = while(Ret = GetMessage(&msgGetMessage(&msg, NULL, 0, 0) , NULL, 0, 0) if (Ret = -1) if (Ret = -1)return;retur
26、n; TranslateMessage(&msgTranslateMessage(&msg);); DispatchMessage(&msgDispatchMessage(&msg);); 26LRESULT LRESULT CALLBACK CALLBACK WindowProc(HWNDWindowProc(HWND hwndhwnd, , UINT UINT uMsguMsg, , WPARAM WPARAM wParamwParam, , LPARAM LPARAM lParamlParam) ) SOCKET Accept; char DataBuf80; memset(DataBu
27、f,0,80); SOCKET Accept; char DataBuf80; memset(DataBuf,0,80); if ( if (uMsguMsg = WM_SOCKET) = WM_SOCKET) /发生发生winsockwinsock消息消息switch(WSAGETSELECTEVENT(lParamswitch(WSAGETSELECTEVENT(lParam) case FD_ACCEPT: case FD_ACCEPT: Accept = Accept = accept(wParamaccept(wParam, NULL, NULL), NULL, NULL) WSAA
28、syncSelect(AcceptWSAAsyncSelect(Accept, , hwndhwnd, WM_SOCKET, , WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE); FD_READ|FD_WRITE|FD_CLOSE); break;break; case FD_READ: / case FD_READ: / 读数据读数据 recv(wParam,DataBuf,80,0); break; recv(wParam,DataBuf,80,0); break; case FD_CLOSE: case FD_CLOSE: closesocket(wParamclosesocket(wParam); break;); break; return return DefWindowProc(hwndDefWindowProc(hwnd, , uMsguMsg, , wParamwParam, , lParamlParam);); 27