Winsock提供了一些I/O模型帮助应用程序以异步方式在一个或者多个套接字字上管理I/O。
大概共有6种:阻塞(blocking)模型、选择(select)模型、WSAAsyncSelect模型、WSAEvenetSelect模型、重叠模型、完成端口模型。
阻塞模式:
套接字字创建时,默认工作在阻塞模式下。当recv接受不到消息时会一直等待,从面造成阻塞。好处:使用简单,适合开始学习。缺点:多个套接字连接时,需要多个线程,实际开发中很少使用。
非阻塞模式:
u_long ul=1; SOCKET s=socket(AF_INET,SOCK_STREAM,0); ioctlsocket(s,FIONBIO,(u_long*)&ul);//三行代码,设置为非阻塞模式。设置为非阻塞模式后,处理发送和接收灵气或管理连接的Winsock调用将都会立即返回。多数情况下,出错代码为WSAEWOULDBLOCK,说明请求的操作在调用期间没有完成。
这就需要多次调用,才会得到想要的结果。就会影响程序的性能。实际开发中也很少使用。
选择模型:
select模型在Winsock中使用很广泛,通过select函数来管理I/O。
select函数可以确定一个或者多个套接字的状态,如果没有发生网络时间,进行等待状态。
int select( int nfds, //忽略 fd_set* readfds, //指向一个套接字集合,用来检查可读性 fd_set* writefds, //指向一个套接字集合,用来检查可写性 fd_set* exceptfds, //指向一个套接字集合,用来检查错误 const struct timeval* timeout //用来指定等待的时间,为NULL,则无限大 );函数调用成功,返回所有套接字总和;等待超时,返回0;失败,返回SOCKET_ERROR。
fd_set结构可以把多个套接字连在一起,形成一个套接字集合。
4个操作fd_set套接字集合的宏:FD_ZERO(*set)初始化为空集合、FD_CLR(s,*set)从set移除套接字s、FD_ISSET(s,*set)检查s是不是set的成员,是返回TRUE、FD_SET(s,*set)添加套接字到集合。
如想要测试s是否可读时,先把它添加到集合中,然后等待select函数返回。select执行完成后,再判断s是否还在集合中,在,就说明是可读的。
具体的编程流程:
(1)、初始化套接字集合fsSocket,向这个集合中添加监听套接字句柄。
(2)、将fdSocket集合的拷贝fdRead传递给select函数,当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套接字句柄(对这话的理解,我是这样认为的,select函数会只保留集合中有需要进行I/O操作的套接字句柄),然后返回。
(3)、比较原来fdSocket集合与select处理过的fdRead集合,确定哪些套接字有未决I/O(哪些套接字有I/O消息),处理这些I/O。
使用select的好处是程序能够在单个线程内同时处理多个套接字连接,避免了阻塞模式下的线程膨胀问题。但是fd_set结构的套接字数据是有限制的,最大值是FD_SETSIZE,在WinSock2.h中是64,当然,这个是可以修改的,但是最大也只是1024。另外,如果FD_SETSIZE太大的话,服务器需要检查太多的套接字,对性能会有影响。
WSAAsyncSelect模型
WSAAsyncSelect模型允许应用程序以Windows消息形式接收网络事件通知。这个模型是为了适用Windows的消息驱动环境而设置的,现在许多性能要求不高的网络应用程序都采用了WSAAsyncSelect模型,MFC中的CSocket类也使用了它。
WSAAsyncSelect函数自动把套接字设为非阻塞模式,并且为套接字绑定一个窗口句柄,当有网络事件发生时,便向这个窗口发送消息。
int WSAAsyncSelect(SOCKET s, //需要设置的套接字句柄 HWND hWnd, //指定一个窗口句柄,套接字的通知消息装被发送到与其对应的窗口过程中 u_int wMsg, //网络事件到来时接收到的消息ID long lEvent //指定哪些通知码需要发送 ); //最后一个参数lEvent指定了要发送的通知码,可以是如下取值的组合 FD_READ //套接字接收到对方发送来的数据包,表明这时可以去读套接字 FD_WRITE //缓冲区满后再次变空时,WinSock接口通过该通知码通知应用程序。表明可以继续发送数据 FD_ACCEPT //监听中的套接字检测到有连接进入 FD_CONNECT //用套接字对连接对方的主机,当连接动作完成以后会接收到这个通知码 FD_CLOSE //检测到套接字对应的连接被关闭WSAAsyncSelect模型最突出的特点是与Windows的消息驱动机制融在了一起,这使得开发带GUI界面的网络程序变得简单。但是如果连接增加,单个Windows函数处理上千个客户请求时,服务器性能势必会受到影响。
WSAEventSelect模型
WSAEventSelect模型与WSAAsyncSelect模型类似,允许应用程序在一个或者多个套接字上接收基于事件的网络通知。它与WSAAsyncSelect模型类似是因为它也接收FD_XXX类型的网络事件,不过并不是依靠Windows的消息机制,而是经由事件对象句柄通知。
基本思路为感兴趣的一组网络事件创建一个事件对象,再调用WSAEventSelect函数装网络事件和事件对象关联起来。当网络事件发生时,Winsock使相应的事件对象受信,在事件对象上的等待函数就会返回。之后,调用WSAEnumNetworkEvents函数便可获取到底发生了什么网络事件。
WSAEVENT WSACreateEvent(void); //返回一个手工重置的事件对象句柄 int WSAEventSelect(SOCKET s, //套接字句柄 WSAEVENT hEventObject, //事件对象句柄 long lNetworkEvents //感兴趣的FD_XXX网络事件的组合 ); //网络事件与事件对象关联之后,应用程序便可以在事件对象上等待了。Winsock提供了下面的函数 DWORD WSAWaitForMultipleEvents(DWORD cEvents, //指定下面lphEvents所指的数组中事件对象句柄的个数 const WSAEVENT* lphEvents, //指定一个事件对象句柄数组 BOOL fWaitAll, //指定是否等待所有事件对象都变成受信状态 DWORD dwTimeOut, //指定要等待的时间,WSA_INFINITE为无穷大 BOOL fAlertable //在使用WSAEventSelect模型时可以忽略,应设为FALSE );
WSAEventSelect模型简单易用,也不需要窗口环境,但缺点就是
WSAWaitForMultipleEvents最多支持64个对象,如果需要管理更多的话,就需要创建线程了。