How can I accept multiple TCP connections in Perl?

I have a problem with a Perl script for Linux. The main goal is to mediate between the three applications. What should he do:

  • It should be able to wait for UDP text (without spaces) on $udp_port
  • When it receives this UDP text, it should send it to the TCP client that is connected

The problem is that my application runs until the first time when I disconnect from the TCP client. Then I can no longer connect to it, and it expires after receiving the next UDP packet on $udp_port . So basically, when I want to connect to TCP, I have to restart the application.

All this should be as fast as possible (every milliseconds are counted). Text sent in UDP or TCP does not contain spaces. It is not necessary to be able to support multiple TCP clients at once, but this will certainly be useful :-)

Here is my current 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"; } 

Here is the code I tried after a couple of sentences to go without threads. The problem is even that I can connect to TCP Client msg "Attempting to configure UDP \ n is never displayed. Maybe I'm doing something wrong. The tcp client just connects and waits for the server to send some data. It is not accepted. Here is the 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"; } 
+4
source share
5 answers

It is not threaded, but I think it does what I think you want:

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

Disclaimer: I just hit this. I think it is very fragile. Do not try to use it in a production environment without much testing and armor protection. He can eat your data. It can try and eat your lunch. Use at your own risk. There is no guarantee.

+3
source

After it disconnects, you probably want to go around again and wait again for a new connection with ->accept .

It would be nice to also use strict; and use warnings; identify any errors.

Edit: And I don't think that glob does everything you think it does there.

+8
source

Try folding your code into the simplest possible program that accepts a TCP connection, disconnects, and then accepts another. When you get to this, everything else just clarifies the details.

Anonymous hints were broken. You have too many errors in the code that you included in your question, so you better start with a simple case and then create one.

A simple TCP listener might look something like this: it just listens on the port on the local host and prints what it sees:

 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"; } 
+6
source

You have some design issues that you need to run into (which has nothing to do with Perl or threads).

As I understand it, your application should receive some UDP messages and send them to a client or clients connected to a TCP socket.

What do you do with UDP messages received when a client is not connected to a TCP socket? Do you save them for delivery to the first TCP client that connects or just drops them?

If the design is simple, i.e. if this is something like strings:

  • Your application serves no more than one TCP client at any one time.
  • Your application expects the client to connect to a TCP socket
  • Once the connection arrives, create a new UDP socket
  • Each time a message is received on a UDP socket, send it over a TCP socket
  • As soon as the TCP client disconnects, merge the UDP socket, return waiting for TCP connections

you don’t need multithreading at all.

+2
source

There are many event loops in CPAN. Take a look at AnyEvent - after you learn to think about “event programming”, then it will be relatively easy (and more flexible than just a non-blocking listener).

+1
source

Source: https://habr.com/ru/post/1299447/


All Articles