2010-04-28 97 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套接字已關閉。