如何在Perl中接受多个TCP连接?

时间:2010-01-26 17:00:54

标签: linux perl multithreading tcp udp

我的Linux Perl脚本有问题。它的主要目的是成为3个应用程序之间的中间人。它应该做什么:

  1. 它应该能够在$udp_port
  2. 上等待UDP文本(不含空格)
  3. 当收到该UDP文本时,它应将其转发到已连接的TCP客户端
  4. 问题是我的应用程序目前有效,直到我第一次断开与TCP客户端的连接。然后我再也无法连接它了,它在$udp_port上收到下一个UDP数据包后超时。因此,基本上每当我想重新连接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客户端消息“尝试设置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 个答案:

答案 0 :(得分:8)

断开连接后,您可能想要循环并再次等待与->accept的新连接。

use strict;use warnings;找出错误也是一个好主意。

编辑:而且我认为glob无论你认为它做了什么。

答案 1 :(得分:6)

尝试将代码简化为最简单的程序,该程序接受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";
}

答案 2 :(得分:3)

它没有线程,但我认为这符合我的想法:

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

            }

        }

    }

}

免责声明:我刚刚说出来。我想它很脆弱。不要在没有太多测试和防弹的情况下在生产环境中尝试使用它。它可能会吃掉你的数据。它可能会尝试吃你的午餐。使用风险由您自己承担。没有保修。

答案 3 :(得分:2)

您需要面对一些设计问题(实际上与Perl或线程无关)。

据我了解,您的应用程序应该接收一些UDP消息并将它们传递到客户端或TCP套接字上连接的客户端。

当TCP套接字上没有连接客户端时,如何处理收到的UDP消息?您是否将它们保存到第一个连接或丢弃它们的TCP客户端?

如果设计很简单,也就是说,如果设计很简单:

  • 您的应用在任何指定时间最多只能为一个TCP客户端提供服务
  • 您的应用等待客户端在TCP套接字上连接
  • 连接到达后,创建一个新的UDP套接字
  • 每次在UDP套接字上收到消息时,都通过TCP套接字发送消息
  • TCP客户端断开连接后,拆除UDP套接字,返回等待TCP连接

你根本不需要任何多线程。

答案 4 :(得分:1)

CPAN上有很多事件循环。看看AnyEvent - 在学习了“事件编程”后,它会相对简单(并且比非阻塞监听器更灵活)。