2010-04-28 109 views
4

该应用程序的目的是侦听特定的UDP多播,然后将数据转发给连接到服务器的任何TCP客户端。该代码工作正常,但我有问题的TCP客户端断开后,套接字不关闭。 socketsniffer实用程序显示套接字保持打开状态,并且所有UDP数据继续被转发给客户端。我相信问题在于“if($ write-> connected())”块,因为它总是返回true,即使TCP客户端不再连接。我使用标准Windows Telnet连接到服务器并查看数据。当我关闭telnet时,假设TCP套接字在服务器上关闭。如果客户端不再连接,Perl不关闭TCP套接字?

为什么connect()将连接显示为活动连接的任何原因,即使它们不是?另外,我应该使用什么替代方案?

代码:

#!/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; 

       } 

      } 

     } 

    } 

} 

回答

5

我不知道Perl或为此事perl的插座什么,但我想不出一个插座API,提供了一个办法知道,如果一个套接字连接的。实际上,我很确定TCP实际上并没有像这样立即知道的方法。这表明连接()并没有告诉你你认为它告诉你什么。 (我不知道,但我敢打赌,它告诉你,你是否已经调用连接/接受或不)

通常插座告诉你,他们已经通过读或写零字节断开 - 你可能想要检查写的返回值,看它是否曾经返回零

+0

即使检查零字节的读写,也不会告诉你TCP套接字是否仍然连接,而不知道另一端有什么(例如,当我发送“foo”时,我总是会收到“bar “在3秒内)。 – mob 2010-04-28 19:16:57

+1

读或写完成时读取或写入的零字节会告诉您另一端发送FIN。你是对的,在没有保持联系的情况下,不可能确定另一端是否已经离开而不关闭连接。 – Stewart 2010-04-28 19:56:06

0

关闭我的头顶,请尝试:

ReuseAddr => 0

我比某些IO ::插座的魔术更是什么导致你的问题。

1

IO :: Socket connected方法只是告诉你套接字是否处于连接状态,即你是否在创建它之后调用它的“连接”。正如斯图尔特所说,没有一种通用的方法来判断TCP套接字的另一端是否掉线,以及您是否“仍然连接”。

2

感谢您的反馈意见。我找到了一个解决方案,似乎对我很好(谢谢斯图尔特)。这与检查返回值一样简单:

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

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