您好,欢迎访问三七文档
第3章Windows套接字I/O模型•阻塞(blocking)模型•选择(select)模型•WSAAsyncSelect模型•WSAEventSelect模型•重叠(overlapped)模型•完成端口(completionport)模型3.1.1阻塞模式•套接字创建时,默认工作在阻塞模式下。例如,对recv函数的调用会使程序进入等待状态,直到接收到数据才返回。第2章的示例程序都是这种情况。•大多数Winsock程序设计者都是从阻塞套接字模式开始学习的,因为这是最容易和最直接的方式。处理阻塞模式套接字的应用程序使用的程序框架便是阻塞模型。此模型是非常容易理解的。•阻塞套接字的好处是使用简单,但是当需要处理多个套接字连接时,就必须创建多个线程,即典型的一个连接使用一个线程的问题,这给编程带来了许多不便。所以实际开发中使用最多的还是下面要讲述的非阻塞模式。3.1.2非阻塞模式•非阻塞套接字使用起来比较复杂,但是却有许多优点。应用程序可以调用ioctlsocket函数显式地让套接字工作在非阻塞模式下,如下代码所示。•u_longul=1;SOCKETs=socket(AF_INET,SOCK_STREAM,0);ioctlsocket(s,FIONBIO,(u_long*)&ul);•一旦套接字被置于非阻塞模式,处理发送和接收数据或者管理连接的Winsock调用将会立即返回。大多少情况下,调用失败的出错代码是WSAEWOULDBLOCK,这意味着请求的操作在调用期间没有完成。例如,如果系统输入缓冲区中没有待处理的数据,那么对recv的调用将返回WSAEWOULDBLOCK。通常,要对相同函数调用多次,直到它返回成功为止。•非阻塞调用经常以WSAEWOULDBLOCK出错代码失败,所以将套接字设置为非阻塞之后,关键的问题在于如何确定套接字什么时候可读/可写,也就是说确定网络事件何时发生。如果需要自己不断调用函数去测试的话,程序的性能势必会受到影响,解决的办法就是使用Windows提供的不同的I/O模型,3.2选择(select)模型•select模型是一个广泛在Winsock中使用的I/O模型。称它为select模型,是因为它主要是使用select函数来管理I/O的。这个模式的设计源于UNIX系统,目的是允许那些想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字。3.2.1select函数•select函数可以确定一个或者多个套接字的状态。如果套接字上没有网络事件发生,便进入等待状态,以便执行同步I/O。函数定义如下。•intselect(•intnfds,//忽略,仅是为了与Berkeley套接字兼容•fd_set*readfds,//指向一个套接字集合,用来检查其可读性•fd_set*writefds,//指向一个套接字集合,用来检查其可写性•fd_set*exceptfds,//指向一个套接字集合,用来检查错误•conststructtimeval*timeout//指定此函数等待的最长时间,如果为NULL,则最长时间为无限大);•函数调用成功,返回发生网络事件的所有套接字数量的总和。如果超过了时间限制,返回0,失败则返回SOCKET_ERROR。•1.套接字集合•fd_set结构可以把多个套接字连在一起,形成一个套接字集合。select函数可以测试这个集合中哪些套接字有事件发生。下面是这个结构在WINSOCK2.h中的定义。3.2.1select函数•1.套接字集合•fd_set结构可以把多个套接字连在一起,形成一个套接字集合。select函数可以测试这个集合中哪些套接字有事件发生。下面是这个结构在WINSOCK2.h中的定义。•typedefstructfd_set{•u_intfd_count;//下面数组的大小•SOCKETfd_array[FD_SETSIZE];//套接字句柄数组}fd_set;下面是WINSOCK定义的4个操作fd_set套接字集合的宏。3.2.1select函数•FD_ZERO(*set)初始化set为空集合。集合在使用前应该总是清空•zFD_CLR(s,*set)从set移除套接字s•zFD_ISSET(s,*set)检查s是不是set的成员,如果是返回TRUEzFD_SET(s,*set)添加套接字到集合3.2.1select函数•网络事件•传递给select函数的3个fd_set结构中,一个是为了检查可读性(readfds),一个是为了检查可写性(writefds),另一个是为了检查错误(exceptfds)。select函数返回之后,如果有下列事件发生,其对应的套接字就会被标识。3.2.1select函数•下面的例子示例了select函数的用法。程序运行之后,在4567端口监听,接受客户端连接请求,打印出接收到的数据。大家可以看到采用select模型之后,即便是在单个线程中,也可以管理多个套接字。具体编程流程如下:•(1)初始化套接字集合fdSocket,向这个集合添加监听套接字句柄。•(2)将fdSocket集合的拷贝fdRead传递给select函数,当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套接字句柄,然后返回。•(3)比较原来fdSocket集合与select处理过的fdRead集合,确定哪些套接字有未决I/O,并进一步处理这些I/O。•(4)回到第2步继续进行选择处理。3.2.1select函数•实际例程3.3WSAAsyncSelect模型•WSAAsyncSelect模型允许应用程序以Windows消息的形式接收网络事件通知。这个模型是为了适应Windows的消息驱动环境而设置的,现在许多对性能要求不高的网络应用程序都采用WSAAsyncSelect模型,MFC(MicrosoftFoundationClass,Microsoft基础类库)中的CSocket类也使用了它。3.3WSAAsyncSelect模型•3.3.1消息通知和WSAAsyncSelect函数•WSAAsyncSelect函数自动把套接字设为非阻塞模式,并且为套接字绑定一个窗口句柄,当有网络事件发生时,便向这个窗口发送消息。函数用法如下。•intWSAAsyncSelect(SOCKETs,//需要设置的套接字句柄HWNDhWnd,//指定一个窗口句柄,•//套接字的通知消息将被发送到与其对应的窗口过程中u_intwMsg,//网络事件到来时接收到的消息ID,•//可以在WM_USER以上的数值中任意选择一个用做ID。longlEvent//指定哪些通知码需要发送);3.3WSAAsyncSelect模型•最后一个参数lEvent指定了要发送的通知码,可以是如下取值的组合:•zFD_READ套接字接收到对方发送过来的数据包,表明这时可以去读套接字了•zFD_WRITE数据缓冲区满后再次变空时,WinSock接口通过该通知码通知应用程序。表示可•以继续发送数据了(短时间内发送数据过多,便会造成数据缓冲区变满)•zFD_ACCEPT监听中的套接字检测到有连接进入•zFD_CONNECT如果用套接字去连接对方的主机,当连接动作完成以后会接收到这个通知码•zFD_CLOSE检测到套接字对应的连接被关闭•例如,在监听套接字上可以这样调用WSAAsyncSelect函数:•::WSAAsyncSelect(sListen,hWnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE);//WM_SOCKET为自定义消息•上述代码将套接字sListen设为窗口通知消息类型。WM_SOCKET为自定义网络通知消息,FD_CLOSE|FD_ACCEPT指定了sListen套接字只接收FD_CLOSE和FD_ACCEPT通知消息。当有客户连接或套接字关闭时,Winsock接口将向指定的窗口发送WM_SOCKET消息。3.3WSAAsyncSelect模型•最后一个参数lEvent指定了要发送的通知码,可以是如下取值的组合:•zFD_READ套接字接收到对方发送过来的数据包,表明这时可以去读套接字了•zFD_WRITE数据缓冲区满后再次变空时,WinSock接口通过该通知码通知应用程序。表示可•以继续发送数据了(短时间内发送数据过多,便会造成数据缓冲区变满)•zFD_ACCEPT监听中的套接字检测到有连接进入•zFD_CONNECT如果用套接字去连接对方的主机,当连接动作完成以后会接收到这个通知码•zFD_CLOSE检测到套接字对应的连接被关闭•例如,在监听套接字上可以这样调用WSAAsyncSelect函数:•::WSAAsyncSelect(sListen,hWnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE);//WM_SOCKET为自定义消息•上述代码将套接字sListen设为窗口通知消息类型。WM_SOCKET为自定义网络通知消息,FD_CLOSE|FD_ACCEPT指定了sListen套接字只接收FD_CLOSE和FD_ACCEPT通知消息。当有客户连接或套接字关闭时,Winsock接口将向指定的窗口发送WM_SOCKET消息。3.3WSAAsyncSelect模型•成功调用WSAAsyncSelect之后,应用程序便开始以Windows消息的形式在窗口函数接收网络事件通知。下面是窗口函数的定义。•LRESULTCALLBACKWindowProc(HWNDhWnd,UINTuMsg,WPARAMwParam,LPARAMlParam);•wParam参数指定了发生网络事件的套接字句柄,lParam参数的低字位指定了发生的网络事件,高字位包含了任何可能出现的错误代码,可以使用宏WSAGETSELECTERROR和WSAGETSELECTEVENT将这些信息取出,这两个宏定义在Winsock2.h文件中。•#defineWSAGETSELECTERROR(lParam)HIWORD(lParam)//高字为出错代码#defineWSAGETSELECTEVENT(lParam)LOWORD(lParam)//低字为通知码如果没有错误发生,出错代码为0,程序可以继续检查通知码,以确定发生的网络事件。3.3WSAAsyncSelect模型•3.3.2应用举例。3.4WSAEventSelect模型•Winsock提供了另一种有用的异步事件通知I/O模型——WSAEventSelect模型。这个模型与WSAAsyncSelect模型类似,允许应用程序在一个或者多个套接字上接收基于事件的网络通知。它与WSAAsyncSelect模型类似是因为它也接收FD_XXX类型的网络事件,不过并不是依靠Windows的消息驱动机制,而是经由事件对象句柄通知。3.4.1WSAEventSelect函数•使用这个模型的基本思路是为感兴趣的一组网络事件创建一个事件对象,再调用WSAEventSelect函数将网络事件和事件对象关联起来。当网络事件发生时,Winsock使相应的事件对象受信,在事件对象上的等待函数就会返回。之后,调用WSAEnumNetworkEvents函数便可获取到底发生了什么网络事件。•Winsock中创建事件对象的函数是WSACreateEvent,定义如下:•WSAEVENTWSACreateEvent(void);//返回一个手工重置的事件对象句柄•创建事件对象之后,必须调用WSAEventSelect函数将指定的一组网络事件与它关联在一起,函数用法如下。•intWSAEventSelect(•SOCKETs,//套接字句柄•WSAEVENThEventObject,//事件对象句柄•longlNetworkEvents//感兴趣的FD_XXX网络事件的组合•);•网络事件与事件对象关联之后,应用程序便可以在事件对象上等待了。Winsock提供了WSAWaitForMultipleEvents函数在一个或多个事件对象上等待,当所等待的事件对象
本文标题:网络通信编程技术3
链接地址:https://www.777doc.com/doc-320123 .html