http://www.perlmonks.org?node_id=968909


in reply to Re^8: UDP connection
in thread UDP connection

You are correct. I must have misread the numbers.

However, there is no problem. Here are a sample server and client that do bidirection communication over 4 socket pairs. The server waits for an initial heartbeat from the client, then starts sending data, and it will stop sending data if it hasn't seen a heartbeat for 10 seconds. There's of course lots of room for improvement, but it does show bidirectional communication is not a problem.

First, the server:

#!/usr/bin/perl use 5.010; use strict; use warnings; use IO::Socket; my $INTERVAL = 10; my $MAX_BUFSIZE = 512; my $NO_CONTACT = 0; my $SENDING = 1; my $LOST_CONTACT = 2; my $server = '192.168.0.1'; my @server_ports = (8020, 8019, 8008, 8003); my @sockets = map { my $socket = IO::Socket::INET::->new( LocalPort => $_, LocalAddr => $server, Proto => 'udp', ) or die $!; $socket; } @server_ports; my @states = ($NO_CONTACT) x @sockets; my @heartbeats = (0) x @sockets; my @counts = (0) x @sockets; my $Rbits = ""; my $Ebits = ""; vec($Rbits, fileno($_), 1) = 1 for @sockets; vec($Ebits, fileno($_), 1) = 1 for @sockets; while (1) { select(my $rbits = $Rbits, undef, my $ebits = $Ebits, 1); for (my $i = 0; $i < @sockets; $i ++) { my $socket = $sockets[$i]; my $fileno = fileno($socket); my $state = $states[$i]; my $heartbeat = $heartbeats[$i]; if (vec($ebits, $fileno, 1)) { say "Got an error on channel $i. I'm out of here"; exit 1; } if (vec($rbits, $fileno, 1)) { my $sender = $socket->recv(my $buffer, $MAX_BUFSIZE); if (length $buffer) { my ($port, $remote) = sockaddr_in($sender); $remote = inet_ntoa($remote); say "Got HB from $remote:$port"; $heartbeat = $heartbeats[$i] = time; if ($state == $NO_CONTACT) { # # Upgrade the socket now we know the remote # vec($Rbits, fileno($socket), 1) = 0; vec($Ebits, fileno($socket), 1) = 0; undef $sockets[$i]; undef $socket; $socket = $sockets[$i] = IO::Socket::INET::->new( LocalPort => $server_ports[$i], LocalAddr => $server, PeerAddr => $remote, PeerPort => $port, Proto => 'udp', ) or die $!; vec($Rbits, fileno($socket), 1) = 1; vec($Ebits, fileno($socket), 1) = 1; $state = $states[$i] = $SENDING; } } } # # Out of time? # if ($state == $SENDING && $heartbeat + $INTERVAL < time) { say "Channel $i is dead. Bye!"; exit 1; } # # Send data? # if ($state == $SENDING) { foreach (1 .. int rand 10) { my $count = ++$counts[$i]; say "Send packet $count on channel $i"; $socket->send($count); } } } } __END__
And the client:
#!/usr/bin/perl use 5.010; use strict; use warnings; use autodie; use IO::Socket; my $server = '192.168.0.1'; my $client = '192.168.0.12'; my @server_ports = ( 8020, 8019, 8008, 8003); my @client_ports = (53036, 53037, 53038, 53039); my $INTERVAL = 10; my $MAX_BUFSIZE = 512; my @sockets; for (my $i = 0; $i < @server_ports; $i ++) { $sockets[$i] = IO::Socket::INET::->new( LocalPort => $client_ports[$i], LocalAddr => $client, PeerPort => $server_ports[$i], PeerAddr => $server, Proto => 'udp', ) or die $!; } my $Rbits = ""; my $Ebits = ""; vec($Rbits, fileno($_), 1) = 1 for @sockets; vec($Ebits, fileno($_), 1) = 1 for @sockets; # # Now loop. If there's data to read, read it. If it's time to send a # heartbeat, do so. # my @heartbeats = (0) x @sockets; while (1) { for (my $i = 0; $i < @sockets; $i++) { if (time >= $heartbeats[$i] + $INTERVAL - 1) { # # Initialize contact, or keep alive # say "Send HB on channel $i"; $sockets[$i]->send("HB"); $heartbeats[$i] = time; } } select(my $rbits = $Rbits, undef, my $ebits = $Ebits, 1); for (my $i = 0; $i < @sockets; $i++) { my $socket = $sockets[$i]; my $fileno = fileno($socket); if (vec($ebits, $fileno, 1)) { say "Got an error on channel $i. I'm out of here"; exit 1; } if (vec($rbits, $fileno, 1)) { # # Read messages, if any # $socket->recv(my $buffer, $MAX_BUFSIZE); next unless length $buffer; say "Received packet $buffer on channel $i"; } } } __END__