如果客户端未连接,Perl是否关闭TCP套接字?

4
应用程序的目的是监听特定UDP组播,然后将数据转发给连接到服务器的任何TCP客户端。代码工作正常,但我有一个问题,即在TCP客户端断开连接后套接字未关闭。套接字嗅探工具显示套接字仍然保持打开状态,并且所有UDP数据都继续转发给客户端。问题我认为在于“if ($write->connected())”块,因为它总是返回true,即使TCP客户端不再连接。我使用标准的Windows Telnet连接到服务器并查看数据。当我关闭Telnet时,TCP套接字应该在服务器上关闭。
任何原因导致connected()显示已断开的连接仍然活动吗?另外,我应该使用什么替代方案?
代码:
    #!/usr/bin/perl

use IO::Socket::Multicast;
use IO::Socket;
use IO::Select;

my $tcp_port = "4550";
my $tcp_socket = IO::Socket::INET->new(
                                       Listen    => SOMAXCONN,
                                       LocalAddr => '0.0.0.0',
                                       LocalPort => $tcp_port,
                                       Proto     => 'tcp',
                                       ReuseAddr => 1,
                                      );
use Socket qw(IPPROTO_TCP TCP_NODELAY);
setsockopt( $tcp_socket, IPPROTO_TCP, TCP_NODELAY, 1);

use constant GROUP => '239.2.0.81';
use constant PORT  => '6550';
my $udp_socket= IO::Socket::Multicast->new(Proto=>'udp',LocalPort=>PORT);
$udp_socket->mcast_add(GROUP) || die "Couldn't set group: $!\n";

my $read_select  = IO::Select->new();
my $write_select = IO::Select->new();

$read_select->add($tcp_socket);
$read_select->add($udp_socket);

## Loop forever, reading data from the UDP socket and writing it to the
## TCP socket(s).  
while (1) {

    ## No timeout specified (see docs for IO::Select).  This will block until a TCP
    ## client connects or we have data.
    my @read = $read_select->can_read();

    foreach my $read (@read) {

        if ($read == $tcp_socket) {

            ## Handle connect from TCP client.  Note that UDP connections are
            ## stateless (no accept necessary)...
            my $new_tcp = $read->accept();
            $write_select->add($new_tcp);

        }
        elsif ($read == $udp_socket) {

            ## Handle data received from UDP socket...
            my $recv_buffer;

            $udp_socket->recv($recv_buffer, 1024, undef);

            ## Write the data read from UDP out to the TCP client(s).  Again, no
            ## timeout.  This will block until a TCP socket is writable.  
            my @write = $write_select->can_write();

            foreach my $write (@write) {

                ## Make sure the socket is still connected before writing.
        if ($write->connected()) {
                     $write->send($recv_buffer);
                   }
                else {
                    $write_select->remove($write);
                    close $write;

                }

            }

        }

    }

}
4个回答

5
我对perl和perl sockets一无所知,但我想不出有哪个socket API可以提供一种方式来知道一个socket是否已连接。实际上,我很确定TCP并没有一种立即知道这一点的方法。这让我觉得connected()并没有告诉你它所声称的内容。(我不知道,但我敢打赌它是在告诉你是否调用了connect/accept)

通常,socket会通过读取或写入零字节来告诉你它们已断开连接——你可能需要检查write的返回值,看它是否返回了零。

即使检查零字节读写,也无法告诉您TCP套接字是否仍然连接,而不“知道”有关另一端的信息(例如,当我发送“foo”时,我将始终在3秒内收到“bar”)。 - mob
1
读取或写入完成时,如果读取或写入的字节数为零,那么说明另一端发送了FIN信号。如果没有保持连接,就无法确定另一端是否已关闭连接。 - Stewart

2

谢谢您的反馈。我已经找到了一个解决方案,它对我来说似乎运行良好(感谢 Stewart)。解决方案很简单,只需要检查返回值:

$resultsend = $write->send($recv_buffer);
if (!$resultsend) {
                $write_select->remove($write);
                close $write;
}

客户端断开连接后,TCP套接字现在已关闭。


1

IO::Socket connected 方法只是告诉你套接字是否处于连接状态,即在创建套接字后是否调用了“connect”。如Stewart所说,没有一般方法可以告诉你TCP套接字的另一端是否掉线,以及你是否“仍然连接”。


0

脑海中想到的,可以尝试:

ReuseAddr => 0

我非常确定 IO::Socket 的魔法是导致你的问题的原因。


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