Как я могу принять несколько соединений TCP в Perl? - PullRequest
5 голосов
/ 26 января 2010

У меня проблема со скриптом Perl для Linux. Его главная цель - быть посредником между 3 приложениями. Что он должен делать:

  1. Должен быть в состоянии ждать текст UDP (без пробелов) на $udp_port
  2. Когда он получает этот текст UDP, он должен переслать его клиенту TCP, который подключен

Проблема в том, что мое приложение работает до тех пор, пока я не отключусь с TCP-клиентом в первый раз. Тогда я больше не могу подключиться к нему, и время ожидания истекает после получения следующего пакета UDP на $udp_port. Таким образом, в основном, когда я хочу восстановить соединение с TCP, я должен перезапустить приложение.

Все это должно быть максимально быстрым (считается каждая миллисекунда). Текст, отправляемый в UDP или TCP, не содержит пробелов. Нет необходимости поддерживать несколько TCP-клиентов одновременно, но это, безусловно, будет преимуществом: -)

Вот мой текущий код:

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;
use Net::hostent;
use threads;
use threads::shared;

my $tcp_port = "10008";  # connection from TCP Client
my $udp_port = "2099";  # connection from Announcer
my $udp_password = ""; # password from Announcer
my $title = "Middle Man server version 0.1";
my $tcp_sock = IO::Socket::INET->new( Proto => 'tcp', LocalPort => $tcp_port, Listen => SOMAXCONN,Reuse => 1)|| die @!;
my $udp_sock = new IO::Socket::INET(LocalPort => $udp_port, Proto => "udp") || die @!;

my (@threads);

print "[$title]\n";

sub mySubTcp($)
{
  my ($popup) = @_;

  print "[TCP][CLIENT CONNECTED]\n";
  while (my $answer = <$popup>)
  {
chomp $answer;
my ($pass, $announce) = split ' ', $answer;
print $answer . '\n';
  }
  printf "[TCP][CLIENT DISCONNECTED]\n";
}

my $client = $tcp_sock->accept();
$client->autoflush(1);


my $thr = threads->new(\&mySubTcp, $client);


while ($udp_sock->recv(my $buf, 1024))
{
  chomp $buf;

  my $announce = $buf;
    print "[ANNOUNCE] $announce [START]\n";
    print $client $announce . "\n";
    print "[ANNOUNCE] $announce [END]\n";

}

Вот код, который я попробовал после нескольких предложений, чтобы идти без потоков. Проблема даже в том, что я могу подключиться с помощью TCP-клиента. Msgstr "Попытка настроить UDP \ n никогда не отображается. Возможно, что-то я делаю не так. Клиент tcp просто подключается и ждет, пока сервер отправит некоторые данные. UDP прибывает, но это не принимаются. Вот код:

#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket;
use Net::hostent;
use threads;
use threads::shared;

my $tcp_port = "10008";  # connection from Tcp
my $udp_port = "2099";  # connection from Announcer

my $title = "Middle Man server version 0.2";
my $tcp_sock = IO::Socket::INET->new( Proto => 'tcp', LocalPort => $tcp_port, Listen => SOMAXCONN,Reuse => 1)|| die @!;

my (@threads);

print "[$title]\n";

for (;;)
{
    my $open_socket = $tcp_sock->accept();
    print "[TCP][CLIENT CONNECTED]\n";
    while (my $input = <$open_socket>)
    {
    print "Trying to setup UDP\n";
    my $udp_sock = new IO::Socket::INET(LocalPort => $udp_port, Proto => "udp") || die @!;
    while ($udp_sock->recv(my $buf, 1024)) {
          chomp $buf;
          print "\[ANNOUNCER\] $buf \[START\]\n";
          print $open_socket $buf . "\n";
          print "\[ANNOUNCER\] $buf \[END\]\n";
    }
    print "Closing UDP\n";
    close $udp_sock;
    #chomp $input;
    #print $input;
}

    close $open_socket;
    printf "[TCP][CLIENT DISCONNECTED]\n";
}

Ответы [ 5 ]

8 голосов
/ 26 января 2010

После того, как он отключится, вы, вероятно, захотите зацикливаться и ждать нового соединения с ->accept снова.

Также было бы неплохо, если бы use strict; и use warnings; выискивали ошибки.

Редактировать: И я не думаю, что glob делает то, что, как вы думаете, делает там.

6 голосов
/ 26 января 2010

Попробуйте свести ваш код в простейшую из возможных программ, которая принимает TCP-соединение, отключается, а затем принимает другое. Когда вы дошли до этого места, все остальное просто уточняет детали.

Намек Анонима был на ура. У вас слишком много мелких ошибок в коде, который вы включили в свой вопрос, поэтому вам лучше начать сначала с простого случая, а затем собрать его.

Простой TCP-прослушиватель может выглядеть примерно так - он просто прослушивает порт на localhost и печатает то, что видит:

use strict; use warnings;
use IO::Socket;
my $socket = IO::Socket::INET->new(
    LocalHost => 'localhost',
    LocalPort => '5555',
    Proto => 'tcp',
    Listen => 1,
    Reuse => 1,
) or die "Could not create socket: $!";

for (;;)
{
    my $open_socket = $socket->accept();
    print "Got a connection!\n";
    while (my $input = <$open_socket>)
    {
        print $input;
    }
    close $open_socket;
    print "Connection closed.\n\n";
}
3 голосов
/ 27 января 2010

Это не с резьбой, но я думаю, что это делает то, что я думаю, что вы хотите:

#!/usr/bin/perl

use strict;
use warnings;

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

my $tcp_port = "10008"; 
my $udp_port = "2099";

my $tcp_socket = IO::Socket::INET->new(
                                       Listen    => SOMAXCONN,
                                       LocalAddr => 'localhost',
                                       LocalPort => $tcp_port,
                                       Proto     => 'tcp',
                                       ReuseAddr => 1,
                                      );

my $udp_socket = IO::Socket::INET->new(
                                       LocalAddr => 'localhost',
                                       LocalPort => $udp_port,
                                       Proto     => 'udp',
                                      );

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).  Might want to install some kind of signal handler to
## ensure a clean shutdown.
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.  What 
            ## happens if no TCP clients are connected?  Will IO::Select throw some
            ## kind of error trying to select on an empty set of sockets, or will the
            ## data read from UDP just get dropped on the floor?  
            my @write = $write_select->can_write(); 

            foreach my $write (@write) {

                ## Make sure the socket is still connected before writing.  Do we also
                ## need a SIGPIPE handler somewhere?
                if ($write->connected()) {
                    $write->send($recv_buffer);
                }
                else {
                    $write_select->remove($write);
                }

            }

        }

    }

}

Отказ от ответственности: я просто ударился об этом. Я думаю, что это очень хрупкий. Не пытайтесь использовать это в производственной среде без особых испытаний и пуленепробиваемости. Это может съесть ваши данные. Это может попытаться съесть ваш обед. Используйте на свой риск. Нет гарантии.

2 голосов
/ 26 января 2010

У вас есть некоторые проблемы с дизайном, с которыми вам нужно столкнуться (которые на самом деле не имеют никакого отношения к Perl или потокам).

Насколько я понимаю, ваше приложение должно получать некоторые UDP-сообщения и передавать их клиенту или клиентам, подключенным к сокету TCP.

Что вы делаете с сообщениями UDP, полученными, когда к сокету TCP не подключен клиент? Вы сохраняете их для доставки первому TCP-клиенту, который подключается, или просто отбрасываете их?

Если конструкция проста, то есть если это что-то вроде:

  • Ваше приложение одновременно обслуживает не более одного TCP-клиента
  • Ваше приложение ожидает подключения клиента к сокету TCP
  • После установления соединения создайте новый сокет UDP
  • Каждый раз, когда сообщение принимается через сокет UDP, отправляйте его через сокет TCP
  • Как только TCP-клиент отключится, разорвите UDP-сокет и вернитесь к ожиданию TCP-соединений

вам вообще не нужна многопоточность.

1 голос
/ 27 января 2010

В CPAN существует множество циклов событий.Взгляните на AnyEvent - после того, как вы научитесь думать о «программировании событий», это будет относительно просто (и более гибко, чем просто неблокирующий слушатель).

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