在涉及套接字I/O操作上设置超时的方法有以下3种。
- 调用alarm, 它在指定超时期满时产生SIGALRM信号。这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm调用。
- 在select中阻塞等待I/O(select有内置的时间限制),以此代替直接阻塞在read或write调用上。
- 使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。这个方法的问题在于并非所有实现都支持这两个套接字选项。
select可用在connect上设置超时的先决条件是相应套接字处于非阻塞模式,而那两个套接字选项对connect并不适用。前两个技术适用于任何描述符,而第三个技术仅使用于套接字描述符。
使用SIGALRM为connect设置超时
先给出源码:
static
int
connect_timeo(int
{
}
static
connect_alarm(int
{
}
本进程的报警时钟设置成由调用者指定的秒数。如果当前已经给本进程设置过报警时钟,那么alarm的返回值是这个报警时钟的当前剩余秒数,且返回一个警告消息,因为我们推翻了先前设置的报警时钟,否则alarm的返回值为0。
调用connect,如果本调用被中断(即返回EINTR错误),那就把errno的值改设为ETIMEOUT,同时关闭套接字,以防三路握手继续进行。
给alarm函数指定参数0,关闭本进程的报警时钟,同时恢复原来的信号处理函数(如果有的话)。
使用SIGALRM为recvfrom设置超时
static
void
dg_cli(FILE
{
}
static
sig_alrm(int
{
}
为SIGALRM建立一个信号处理函数,并在每次调用recvfrom前调用alarm设置一个5秒钟的超时。如果recvfrom被我们的信号处理函数中断了,那就输出一个信息并继续执行。如果读到一行来自服务器的文本,那就关掉报警时钟并输出服务器的应答。
使用select为recvfrom设置超时
下面这个readable_timeo函数等待一个描述符最多在指定的秒数内变为可读。
int
readable_timeo(int
{
}
int
Readable_timeo(int
{
}
select等待该描述符变为可读,或者超时。本函数的返回值就是select的返回值;
出错为-1,超时发生时为0,否则返回的正值给出已就绪描述符的数目。
下面是dg_cli函数的原型:
void
dg_cli(FILE
{
}
该函数调用Readable_timeo函数,当返回值大于0时才调用recvfrom,确保recvfrom不会阻塞。
使用SO_RCVTIMEO套接字选项为recvfrom设置超时
本选项一旦设置到某个描述符(包括指定超时值),其超时设置将应用于该描述符的所有读操作。本方法的优势就体现在一次性设置选项上,而前两个方法总是要求我们在欲设置时间限制的每个操作发生之前做些工作。本套接字选项仅仅应用于读操作,类似的SO_SNDTIMEO选项则仅仅应用于写操作,两者都不能用于为connect设置超时。
下面是使用SO_RCVTIMEO套接字选项的另一个版本的dg_cli函数。
void
dg_cli(FILE
{
}
如果I/O操作超时,其函数(这里是recvfrom)将返回一个EWOULDBLOCK错误。