Windows Sockets编程(三)操作模式1

来源:互联网 发布:小站托福有mac版吗 编辑:程序博客网 时间:2024/06/07 22:06

1、Windows Sockets API的3种操作模式:阻塞的、非阻塞的、异步的。

类比:设想你拨通了一个电话,但是对方这会不在电话旁,此时你的选择可以是:

不挂机,等待,直到对方过来接电话。

挂机,之后再打过去。

留言,让对方打过来。

1)不挂机,等待:阻塞

此模式下,Windows Sockets函数只有当操作完成时才返回,结果可能成功,也可能失败,这样能创建的应用程序就是有局限性的。比如当一个阻塞式调用没有完成时,你不能在任务(或线程)中调用任何其他的WinSock函数,你要接收消息就会成为问题。

2)挂机后再拨:非阻塞

在操作完成过程中需要“探询”,但太频繁会浪费系统资源。此模式下WInSock函数立即返回,有些情况下返回值表示成功,但又是却表示失败。但函数调用并非全是坏事,错误值(调用WSAGetLastError)有可能是WSAEWOULDBLOCK,即“如果必须等待操作完成才能返回,那么函数应该阻塞猜对”。

其有两种含义:一是操作还在等待中,二是WInSock做了尝试,但在当前无法满足操作请求。前者时操作总要完成,并有方法来检测操作是否完成。后者时表示用户还需要再次调用函数。

3)请求对方回拨:异步

此模式是非阻塞的,因为它在操作完成之前就从调用返回了。此时WSAEWOULDBLOCK的含义是:当一个等待处理的操作完成时,或者当重新发起一个操作时,WINSock DLL将发送一个消息(即对方回拨你)来通知你。


建议选择异步操作模式!


一、非阻塞模式

1、无论涉及的操作是否完成,一个非阻塞模式的函数调用都立即返回。大多数非阻塞模式的应用程序都是混合式的,通过采用非阻塞socket和阻塞的select函数来更有效的复用多个socket。

2、函数socket调用所返回的socket句柄在I/O操作时默认是阻塞的。应用程序可以隐式地或显式的将socket变为非阻塞的。如果调用WSAAsyncSelect(),能够自动地使socket变为非阻塞的。其等同于采用异步操作模式。

为了显式的将socket设为非阻塞的,可以调用设置了FIONBIO命令的ioctlsocket()函数。

3、非阻塞函数调用失败时返回一个错误代码,该代码指示成功或当前的socket状态。另一些函数调用可能看起来成功了,但却并没有完成所请求的操作。

1)WSAEWOULDBLOCK错误:

如果一个函数调用失败,WSAGetLastError()返回的此错误,那么这个函数调用并没有失败。根据函数的不同,此错误说明WInSock DLL已经开始了此操作,或者说明你需要稍后再次调用该函数。

2)部分成功:

即使非阻塞socket函数的返回值表明成功,也不意味着确实完成了所要求的操作。例如,函数send、sendto、recv和recvfrom的返回值有可能小于你在输入参数中传递的长度值。所以此时,不仅需要检查成功与否,还要将返回值与你请求传输的数据长度进行比较。

4、探询而非阻塞:

检测何时开始一个操作、当函数失败并返回WSAEWOULDBLOCK后,检测操作是否完成。

5、显式地避让:

Windows Socket的阻塞操作具有隐含的避让机制。非阻塞操作却能够阻塞系统。

解决方案之一是:建立混合式的应用程序,此时只有当检测当前socket状态时采用阻塞的select()函数,其余的操作全用非阻塞socket。之二是创建自己的阻塞钩子函数,可以按照要求进行避让。


二、异步模式

1、窗口是对象,对象是不透明的,其内部细节隐藏在背后。对象之间通过传递消息来通信,而消息是异步到达的,对象则对消息进行响应。Windows Socket的异步操作模式利用Windows的消息机制而非回调机制。

通过采用异步操作模式,应用程序可以把工作“卸载”下来给WInSock DLL去处理,利用Windows的消息驱动架构而不是简单的去对付他。

2、异步函数及其描述:

WSAAsyncGetHostByAddr():根据主机地址检索主机信息

WSAAsyncGetHostByName():根据主机名检索主机信息

WSAAsyncGetProtoByName():根据协议名检索协议号

WSAAsyncGetProtoByNumber():根据协议号检索协议名

WSAAsyncGetServByPort():根据端口号检索服务信息。

WSAAsyncSelect():请求WINSock DLL通知应用程序一个或多个“事件”,诸如连续完成、输出缓存区可用,以及数据到达

3、WSAAsyncGetXByY()

其代表的是异步数据库函数,无论外形还是实际操作都很相似。

HANDLE PASCAL FAR WSAAsyncGetHostByName(

       HWND hWnd,

       unsigned int wMsg,

       char FAR *name,

       char FAR *buf,

       int buflen);

除了需要解析的对象外,异步的数据库函数(用于主机名、协议和服务解析)都带有一个Windows句柄、一个消息和作为输入参数的一个缓存。这些函数能够在操作完成之前立即返回,返回值是一个能够唯一识别该查询的句柄(这样就能与响应匹配),如果在处理请求时出错,则返回0值。

4、上述操作过程总结如下:
1)应用程序发送解析请求,等待通知解析事件。

2)函数返回一个查询句柄,如果不能处理查询时返回0。

3)查询完成时,WInSock DLL给应用程序发送一个消息,指示解析成功或失败。

5、除了请求对一个或多个事件的异步通知外,WSAAsyncSelect()还自动地把socket设为非阻塞模式,并且不返回句柄(socket就是句柄)。

6、撤销异步操作:
任何WSAAsyncGetXByY()函数发起的异步操作,都可以使用WSACancelAsyncRequest()来撤销。但不能撤销WSAAsyncSelect函数发起的异步操作。

int PASCAL FAR WSACancelAsyncRequest(

         HANDLE hAsyncTaskHandle);

参数为将撤销的异步操作的句柄,即由WSAAsyncGetXByY()调用返回的。

成功时返回0值,失败时返回SOCKET_ERROR。常见的是WSAEINVAL(句柄不可用)和WSAEALREADY(异步操作已经撤销)。

7、如果应用程序异步操作正在等待处理时调用WSACleanup(),该异步操作将被撤销(并不发送任何通知)。

8、三种模式总结比较:

非阻塞模式是最快的,但开销最大,也是最不友好的。

异步操作能够很快地传输数据并且同时保持友好性。

阻塞模式是最友好的,但相对较慢。


0 0
原创粉丝点击