网络通信模型(一)

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个对象,如果需要管理更多的话,就需要创建线程了。

你可能感兴趣的:(网络通信模型(一))