TCP连接超时(Unix/Windows通用)

5

我正在使用Perl(希望不会影响任何内容),但我需要知道如何设置连接操作的超时时间。问题在于,我不能永远等待连接操作完成。如果它在几秒钟内没有完成,我宁愿放弃并继续进行下一步。

socket(my $sock, PF_INET, SOCK_STREAM, (getprotobyname('tcp'))[2]);
setsockopt($sock, SOL_SOCKET, SO_SNDTIMEO, 10); # send timeout

print "connecting...\n";
connect($sock, sockaddr_in(80,scalar gethostbyname('lossy.host.com')));
print "connected...\n";

问题在于,如果与“lossy.host.com”的连接是“不稳定或缓慢或除了快速以外的其他情况”,我宁愿放弃而不是让用户等待。(把它想象成一个执行其他操作的程序的副作用...用户可能不希望这个脚本与某个服务器通信...)

线程案例:您将如何中断connect()?您只需分离线程并忘记它吗?

3个回答

2
您可以使用fcntl将套接字设置为非阻塞,然后使用带有超时的select等待它变得可读。如果在超时之前它不可读,您可以在那时关闭它。
我知道如何在C中做到这一点,但不知道perl,否则我会给您提供一个示例。 perlfunc手册中说所有这些函数都存在,并且初步阅读似乎表明它们将按照您的要求工作。
编辑:抱歉,错过了perlfunc中说明它们可能不适用于非Unix系统的部分,实际上,fcntl在win32上不可用。但是,在Windows上可以使用IO::Socket库来完成正确的事情。
这是适用于我的样本代码(在Linux上):
#!/usr/bin/perl

use IO::Socket::INET;
use IO::Select;

$sock = IO::Socket::INET->new('PeerAddr' => 'lossy.host.com',
                              'PeerPort' => 80,
                              'Blocking' => 0 );

$sel = IO::Select->new( $sock );

@writes = $sel->can_write(10);

if ( $sock->connected ) {
    print "socket is connected\n";
} else {
    print "socket not connected after however long\n";
    $sock->close;
}

你能具体说明一下吗?你会如何使用IO::Socket库来完成这个任务?将套接字设置为非阻塞的? - dlamotte
没错,当你构建套接字时,将“Blocking”设置为0(即非阻塞)。它应该使用非阻塞连接。 - Eric Warmenhoven
我尝试了这个,但不知何故在我的Windows电脑上无法正常工作... 你有在Windows上实际尝试过吗? - dlamotte
我只在Linux上测试过。不幸的是,我没有Windows电脑可以测试,否则我会的。不过,通过文档查看,它应该可以工作。你知道它失败在哪里吗? - Eric Warmenhoven

1
你可以创建一个独立的线程来执行操作,并进行定时等待结果。如果在适当的时间内没有得到结果,放弃等待并让线程继续运行。它最终会超时,或者你可能能够杀死该线程。
回答最初的问题,我不认为有一种方法可以改变connect()超时时间,至少不能通过套接字API实现。在Windows上,如果有一个注册表键可以更改以影响它,我不会感到惊讶,但我不知道是什么。

0
如果您最终采用了线程方式,其中您分离了连接线程而没有将其杀死,请注意以下内容:Windows仅允许您最多有10个挂起的传出TCP连接(第11个将阻塞,直到其中一个挂起的连接超时)。
这给我带来了很多挫败感。我认为微软之所以这样做是为了防止僵尸网络传播之类的事情。我不认为有任何方法可以关闭它。

听起来像是“Windows 的事情”。不过还是谢谢!我会把它和其他 Windows 的小技巧放在脑后的。;) - dlamotte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接