Perl не закрывает TCP-сокеты, если клиенты больше не подключены? - PullRequest
4 голосов
/ 28 апреля 2010

Целью приложения является прослушивание определенной многоадресной рассылки UDP, а затем пересылка данных любым TCP-клиентам, подключенным к серверу. Код работает нормально, но у меня есть проблема с сокетами, не закрывающимися после разъединения клиентов TCP. Утилита socketsniffer показывает, что сокеты остаются открытыми, а все данные 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 голосов
/ 28 апреля 2010

Я ничего не знаю о perl или сокетах perl в этом отношении, но я не могу думать о API сокетов, который предоставляет способ узнать, подключен ли сокет. На самом деле, я почти уверен, что у TCP нет способа узнать об этом сразу. Это говорит мне о том, что метод connect () не говорит вам того, о чем вы думаете. (Понятия не имею, но держу пари, он говорит вам, звонили ли вы, звоните или принимайте)

Обычно сокеты сообщают вам, что они отключились, читая или записывая нулевые байты - вы можете проверить возвращаемое значение write, чтобы увидеть, вернет ли оно когда-либо ноль

2 голосов
/ 29 апреля 2010

Спасибо за отзыв. Я нашел решение, которое, кажется, хорошо работает для меня (спасибо Стюарт). Это было так же просто, как проверить возвращаемое значение:

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

Сокеты TCP теперь закрыты после отключения клиента.

1 голос
/ 28 апреля 2010

Метод IO :: Socket connected просто сообщает вам, находится ли сокет в подключенном состоянии, то есть, вызвали ли вы «connect» на нем после того, как создали его. Как сказал Стюарт, нет общего способа определить, пропал ли другой конец сокета TCP и «все еще подключены».

0 голосов
/ 28 апреля 2010

С макушки головы, попробуйте:

ReuseAddr => 0

Я более чем уверен, что магия IO :: Socket является причиной ваших проблем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...