how to use AnyEvent::Handler with socket that has port reuse

anyevent, event-driven, perl, sockets

Solution

I can't run the following because Windows doesn't provide `SO_REUSEPORT`, but I'm extremely confident that the following does what you requested.

That said, I'm not sure it will help. From what I've read, `SO_REUSEPORT` allows you to bind to an already active port, but you're not binding to any port.

use strict;
use warnings;

use AnyEvent           qw( );
use AnyEvent::Handle   qw( );
use AnyEvent::Impl::EV qw( );
use AnyEvent::Socket   qw( tcp_connect );
use Socket             qw( SOL_SOCKET SO_REUSEPORT );
sub transaction {
   my ($host, $port) = @_;
   tcp_connect($host, $port, sub {
      my ($sock) = @_
         or die "Can't connect: $!";

      my $handle;
      $handle = AnyEvent::Handle->new(
         fh => $sock,
         on_eof => sub {
            $handle->destroy();
         },
         on_read => sub {
            my ($handle) = @_;
            print $handle->rbuf();

            # This should continue to read until header +
            # Content-Length bytes have been read instead
            # of stopping after one read.
            $handle->destroy();
         },
      );

      $handle->push_write("GET / HTTP/1.1\r\nHost: $host\r\n\r\n");
      $handle->push_shutdown();  # Done writing.
   }, sub {
      my ($sock) = @_;

      setsockopt($sock, SOL_SOCKET, SO_REUSEPORT, 1)
         or die $!;

      return undef;
   });
}
{
   my $cv = AnyEvent->condvar();

   my $t = AnyEvent->timer(after=>0.001, interval=>0.001, cb=> sub {
      transaction("localhost", $ARGV[0] // die("usage"));
   });

   $cv->recv();
}

Server used for testing:

use strict;
use warnings;
use 5.010;

use IO::Socket::INET qw( );
use Socket           qw( inet_ntoa );

my $serv = IO::Socket::INET->new(
   Listen => 1,
);

say inet_ntoa($serv->sockaddr) . ":" . $serv->sockport;

while (my $client = $serv->accept()) {
   say "Connection from ".inet_ntoa($client->peeraddr).":".$client->peerport;
   while (<$client>) {
      last if /^(?:\r?\n)?\z/;
   }

   say $client "HTTP/1.1 200 OK\r\n"
      .        "Content-Type: text/plain\r\n"
      .        "\r\n"
      .        "Hello\n";

   say "   done.";
}

Problem

Recently I ran into a great perl module "AnyEvent", which allows user to do asynchronous/event-driven programing. Created the following snippet which works fine. The problem I have is that after it opens and closes lots sockets, it quickly exhausted all the client ports ("netstat -ant" shows 20,000+ sockets are in TIME_WAIT state). ``` $hdl = new AnyEvent::Handle ( connect => [$ip, $port], on_connect=> sub { my ($handle, $host, $port, $tmp) = @_; #print "connect routine for $handle->{ue}\r\n"; #update states. }, on_read => sub { my $hdl = $_[0]; #read data #send response. }); ``` I wonder if it's possible to create TCP socket with IO::Socket::INET and then use the newly created socket in AnyEvent::Handle: ``` my $sock = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => $ue->{vars}->{ip}, PeerPort => $ue->{vars}->{dstPort}, ReusePort => 1, KeepAlive => 1 ) || die "failed to setup outsock $@\n"; $hdl = new AnyEvent::Handle ( fh => $sock, on_connect=> sub { my ($handle, $host, $port, $tmp) = @_; #print "connect routine for $handle->{ue}\r\n"; #update states. }, on_read => sub { my $hdl = $_[0]; #read data #send response. }); ``` Tried it but it doesn't work. Appreciate any suggestions/comments. Thanks to ikegami who looked into it and gave an suggestion. However, it seems that SO_REUSEADDR doesn't take effect. Here is the code I used (based on his suggestion) ``` use strict; use warnings; use AnyEvent qw( ); use AnyEvent::Handle qw( ); use AnyEvent::Impl::EV qw( ); use AnyEvent::Socket qw( tcp_connect ); use Socket qw( SOL_SOCKET SO_REUSEPORT SO_REUSEADDR); my $ts = 0; my $trans = 0; my $currentTS; sub transaction { my ($host, $port) = @_; tcp_connect($host, $port, sub { my ($sock) = @_ or die "Can't connect: $!"; my $handle; $handle = AnyEvent::Handle->new( fh => $sock, on_eof => sub { $handle->destroy(); }, on_read => sub { my ($handle) = @_; #print $handle->rbuf(); $trans ++; $currentTS = time(); if ($currentTS > $ts) { $ts = $currentTS; print "$trans\n"; } #printf "recved %d bytes of data\n", length($handle->rbuf); # This should continue to read until header + # Content-Length bytes have been read instead # of stopping after one read. if (length($handle->rbuf) > 0) { $handle->destroy(); } }, ); $handle->push_write("GET /s HTTP/1.1\r\nHost: $host\r\n\r\n"); #$handle->push_shutdown(); # Done writing. }, sub { my ($sock) = @_; #setsockopt($sock, SOL_SOCKET, SO_REUSEPORT, 1) or die $!; setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, 1) or die $!; # die "failed to set linger $!\n"; return undef; }); } { my $cv = AnyEvent->condvar(); my $t = AnyEvent->timer(after=>0.001, interval=>1, cb=> sub { transaction("10.3.0.6", 80 ); }); $cv->recv(); } ``` My system is Ubuntu 11.04. In directory /proc/sys/net/ipv4, here are the content of two files: % more tcp_tw_recycle 1 % more tcp_tw_reuse 1

Original source