分类: LINUX
2009-09-10 17:17:36
在网络通讯中,socket处于阻塞模式运行时,其存在着超时处理。以下总结下在那些阻塞函数的处理方法。
这里摘抄一段描述阻塞函数的描述,非常到位。
所谓阻塞函数,是指其完成指定的任务之前不允许程序调用另一个函数,在Windows下还会阻塞本线程消息的发送。 所谓非阻塞函数,是指操作启动之后,如果可以立即得到结果就返回结果,否则返回表示结果需要等待的错误信息,不等待任务完成函数就返回。 首先,异步函数是非阻塞函数; 其次,获取远地信息的数据库函数是阻塞函数(因此,WinSock提供了其异步版本); 在Berkeley socket函数部分中,不涉及网络I/O、本地端工作的函数是非阻塞函数; 在Berkeley socket函数部分中,网络I/O的函数是可阻塞函数,也就是它们可以阻塞执行,也可以不阻塞执行。这些函数都使用了一个socket,如果它们使用的socket是阻塞的,则这些函数是阻塞函数;如果它们使用的socket是非阻塞的,则这些函数是非阻塞函数 |
其实说明阻塞还是非阻塞也是我们可以设置相关。这里主要讲解下处于阻塞模式下的超时处理。
1.在我们直接调用socket创建时,如果不进行特意声明的话,创建的socket都是阻塞的。这样当我们调用accept,recv时,将有可能“block”,如果想设置为非阻塞,则方法有调用fcntl,select,WSAAsynSelect 来改变socket的阻塞
hsocket = socket(AF_INET, SOCK_STREAM, 0)
fcntl(hsocket, F_SETFL, 0_NONBLOCK);
注意:
其中fcntl是Unix系统环境中使用的,使用ioctl()函数和fcntl()函数实现对套接字的控制,而在Windows系统中则应使用ioctlsocket()函数。
Ioctl和fcntl的区别是:
ioctl - control device
ioctl() performs a variety of control functions on devices
and STREAMS. For non-STREAMS files, the functions performed
by this call are device-specific control functions. request
and an optional third argument with varying type are passed
to the file designated by fildes and are interpreted by the
device driver.
The fcntl() function provides control of open file descriptors. It is similar to ioctl().
( 这些带着unix的体味的函数,看着就头大。这个我是摘自某个文档,设置超时应该采用ioctlsocket。)
1.调用MFC的CAsyncSocket和CSocket类
MFC提供了两个类CAsyncSocket和CSocket来封装WinSock API,
CAsyncSocket在较低层次上封装了WinSock API,缺省情况下,使用该类创建的socket是非阻塞的socket,所有操作都会立即返回,如果没有得到结果,返回WSAEWOULDBLOCK,表示是一个阻塞操作。
CSocket建立在CAsyncSocket的基础上,是CAsyncSocket的派生类。也就是缺省情况下使用该类创建的socket是非阻塞的socket,但是CSocket的网络I/O是阻塞的,它在完成任务之后才返回。CSocket的阻塞不是建立在“阻塞”socket的基础上,而是在“非阻塞”socket上实现的阻塞操作,在阻塞期间,CSocket实现了本线程的消息循环,因此,虽然是阻塞操作,但是并不影响消息循环,即用户仍然可以和程序交互。(即程序不会freeze)。
其中设置超时函数如下:
CAsyncSocket::SetSockOpt( int nOptionName, const void* lpOptionValue, int nOptionLen, int nLevel = SOL_SOCKET );
nOptionName用SO_SNDTIMEO or SO_RCVTIMEO。
2.采用select处理超时
int select(int nfds, fd_set FAR* readfds, fd_set FAR* writefds,fd_set FAR*exceptfds, const struct timeval FAR* timeout)
其中在windows中,其nfds可以可以设置为0,没有实际意义。
fd_set fdR;
struct timeval timeout = ..; //设置超时时间
...
for(;;) {
FD_ZERO(&fdR);
FD_SET(sockfd, &fdR);
switch (select(sockfd + 1, &fdR, NULL, &timeout)) {
case -1:
//错误,需要关闭端口。。。
case 0:
// timeout 处理
default:
if (FD_ISSET(sockfd)) {
//或读写操作或Accept()操作,按你设置处理。
}
}
}
注意的是:
由于Windows Sockets 某些函数在接口上虽然与Unix Sockets一致,但是它们的内部实现却不一样,例如,在函数select()的参数中,Unix Sockets实现套接字集合使用的是位掩码,但在Windows Sockets中却是使用一个SOCKET的数组。虽然套接字的集合仍由fd_set类型表示,但在Unix Sockets 源文件中直接修改fd_set结构的代码在Windows Sockets环境下将不能正常工作。故fd_set在微软中则采用FD_XXX宏处理。
3.采用WSAsyncSelect()
这个是消息事件模式来处理的,通过其中参数句柄返回一个消息,在自定义消息中处理。
4.采用setsockopt设置超时
int PASCAL FAR setsockopt( SOCKET s, int level, int optname, s:标识一个套接口的描述字。 |
setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
有两种套接口的选项:
一种是布尔型选项,允许或禁止一种特性;
一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。
对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。
SO_LINGER选项用于控制下述情况的行动:套接口上有排队的待发送数据,且closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。应用程序通过创建一个linger结构来设置相应的操作特性。
这个setsockopt的水也太深了,对于一般运用大致运用就行,主要是其中参数太多,只需要了解几个就可以了,至于想了解更多,则读msdn吧,以及相关的文档吧,不过感觉这些东西纯粹就是个技术指标细节,不需要动脑筋却需要了解的一个方面,说白了是这些都是技术活而不是脑力活。
常见的命令:
//确定套接字自动读入的数据量
#define FIONREAD _IOR(''''f'''', 127, u_long) /* get # bytes to read */
//允许或禁止套接字的非阻塞模式,允许为非0,禁止为0
#define FIONBIO _IOW(''''f'''', 126, u_long) /* set/clear non-blocking i/o */
//确定是否所有带外数据都已被读入
#define SIOCATMARK _IOR(''''s'''', 7, u_long) /* at oob mark? */
设置接受超时