5 Stimmen

Wie kann ich in Perl mehrere TCP-Verbindungen annehmen?

Ich habe ein Problem mit einem Perl-Skript für Linux. Sein Hauptzweck ist es, als Vermittler zwischen 3 Anwendungen zu fungieren. Was es tun sollte:

  1. Es sollte in der Lage sein, auf UDP-Text (ohne Leerzeichen) zu warten $udp_port
  2. Wenn er diesen UDP-Text empfängt, sollte er ihn an den TCP-Client weiterleiten, der mit ihm verbunden ist

Das Problem ist, dass meine Anwendung derzeit funktioniert, bis ich zum ersten Mal die Verbindung mit dem TCP-Client trenne. Dann kann ich mich nicht mehr mit ihr verbinden, und sie bricht ab, nachdem sie das nächste UDP-Paket auf $udp_port . Jedes Mal, wenn ich eine neue TCP-Verbindung herstellen möchte, muss ich die App neu starten.

All dies sollte so schnell wie möglich geschehen (jede Millisekunde zählt). Der an UDP oder TCP gesendete Text darf keine Leerzeichen enthalten. Es ist nicht notwendig, mehrere TCP-Clients auf einmal zu unterstützen, aber es wäre sicherlich von Vorteil :-)

Hier ist mein aktueller Code:

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

}

Hier ist der Code, den ich nach einigen Vorschlägen versucht, ohne Threading zu gehen. Das Problem ist, auch wenn ich in der Lage bin, eine Verbindung mit TCP Client msg "Trying to setup UDP \n wird nie angezeigt. Wahrscheinlich mache ich etwas falsch. Der TCP-Client stellt nur eine Verbindung her und wartet darauf, dass der Server einige Daten sendet. Udp kommt an, aber es wird nicht akzeptiert. Hier ist der Code:

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

8voto

Anonymous Punkte 45953

Nachdem die Verbindung getrennt wurde, sollten Sie eine Schleife bilden und auf eine neue Verbindung mit ->accept wieder.

Es wäre auch eine gute Idee, wenn use strict; et use warnings; um eventuelle Fehler aufzuspüren.

Edita: Und ich glaube nicht, dass glob tut, was immer Sie denken, dass es dort tut.

6voto

Ether Punkte 51044

Versuchen Sie, Ihren Code auf ein möglichst einfaches Programm zu reduzieren, das eine TCP-Verbindung annimmt, die Verbindung trennt und dann eine weitere annimmt. Wenn Sie so weit gekommen sind, ist alles andere nur noch eine Verfeinerung der Details.

Die Hinweise von Anonymous waren goldrichtig. Sie haben viel zu viele kleine Fehler in dem Code, den Sie in Ihrer Frage enthalten, so dass Sie besser von vorne anfangen mit einem einfachen Fall und dann bauen es auf.

Ein einfacher TCP-Listener könnte etwa so aussehen - er lauscht einfach an einem Port auf localhost und gibt aus, was er sieht:

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";
}

3voto

Mark Johnson Punkte 13564

Es hat zwar kein Gewinde, aber ich glaube, es erfüllt Ihre Wünsche:

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

            }

        }

    }

}

Haftungsausschluss: Das habe ich gerade rausgehauen. Ich kann mir vorstellen, dass es sehr zerbrechlich ist. Versuchen Sie nicht, das in einer Produktionsumgebung zu verwenden, ohne es ausgiebig zu testen und kugelsicher zu machen. Es könnte Ihre Daten fressen. Es könnte versuchen, Ihr Mittagessen zu essen. Verwendung auf eigene Gefahr. Keine Garantie.

2voto

Sinan Ünür Punkte 114993

Sie haben einige Design-Probleme, mit denen Sie sich auseinandersetzen müssen (die nichts mit Perl oder Threads zu tun haben, wirklich).

So wie ich es verstehe, soll Ihre Anwendung einige UDP-Nachrichten empfangen und sie an einen oder mehrere über einen TCP-Socket verbundene Clients weiterleiten.

Was tun Sie mit UDP-Nachrichten, die Sie erhalten, wenn kein Client mit dem TCP-Socket verbunden ist? Speichern Sie sie, um sie an den ersten TCP-Client zu senden, der eine Verbindung herstellt, oder verwerfen Sie sie einfach?

Wenn das Design einfach ist, das heißt, wenn es in etwa so aussieht:

  • Ihre Anwendung bedient zu jeder Zeit höchstens einen TCP-Client
  • Ihre Anwendung wartet darauf, dass ein Client eine Verbindung zum TCP-Socket herstellt
  • Sobald eine Verbindung eingeht, erstellen Sie einen neuen UDP-Socket
  • Jedes Mal, wenn eine Nachricht über den UDP-Socket empfangen wird, wird sie über den TCP-Socket gesendet
  • Sobald der TCP-Client die Verbindung trennt, wird der UDP-Socket abgebaut, und es wird wieder auf TCP-Verbindungen gewartet.

brauchen Sie überhaupt kein Multithreading.

2voto

Ask Bjørn Hansen Punkte 6419

Es gibt viele Ereignisschleifen auf CPAN. Werfen Sie einen Blick auf AnyEvent -- nachdem Sie gelernt haben, in "Ereignisprogrammierung" zu denken, wird es relativ einfach sein (und flexibler als nur ein nicht-blockierender Listener).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X