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

Adamba has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to write a script that will HTTP get some urls but with specific source ports.

The idea of using a specific port is to mark with dscp my packets

I've tried to use WWW::Curl::Multi and AnyEvent::HTTP, but i'm always ending with the same problem, the socket is hanging with TIME_WAIT, and I can't reuse the source port that the socket is stuck with, i'm getting this error msg: bind failed with errno 98: Address already in use
I've tried each of the fixes from google, like changing tcp_tw_recycle and tcp_tw_reuse, but it still not working...

The last option that i'm trying is using IO::Socket::INET with Blocking 0, but I'm having trouble with my code:

#!/usr/bin/perl use strict; use warnings; use IO::Socket; use IO::Select; my @ports = qw/65051 65071 65052 65072 65081 65088 65082 65039/; my @hosts = ( "example.com", "google.com", ); my $readers = IO::Select->new(); foreach my $host (@hosts) { foreach my $port (@ports) { print "* Trying: $host\n"; my $socket = IO::Socket::INET->new( PeerAddr => $host, PeerPort => "http(80)", LocalPort => $port, Proto => 'tcp', ReuseAddr => 1, Blocking => 0, ) or die "Couldn't connect to server: $!"; $readers->add($socket); print $socket "GET / HTTP/1.0\r\n\r\n"; $socket = undef; } print "\n"; } while (1) { foreach my $sock (my @ready_for_read = $readers->can_read()) { my @recv_lines = <$sock>; print join "\n", map { s/\r?\n//; $_ } @recv_lines; print "\n"; $readers->remove($sock); $sock->close; } }

Where have I got wrong?

Thanks in advance, Adam

Replies are listed 'Best First'.
Re: Asynchronous HTTP Request using specific ports
by zwon (Abbot) on Apr 04, 2013 at 11:37 UTC

    I tried and the following example works for me:

    use strict; use warnings; use IO::Socket::INET; my $sock1 = IO::Socket::INET->new( PeerAddr => '127.0.0.1', PeerPort => 7777, LocalPort => 6666, ReuseAddr => 1, ) or die $!; my $sock2 = IO::Socket::INET->new( PeerAddr => '127.0.0.1', PeerPort => 7778, LocalPort => 6666, ReuseAddr => 1, ) or die $!; say $sock1 "foo"; say $sock2 "bar"; sleep 100;
    Probably it's something specific to your system

      If you try it in synchronous way, it will work.
      That I know for sure. I want to do it async...

        If I try it asynchronous way it still works:

        use 5.010; use strict; use warnings; use AnyEvent; use AnyEvent::Handle; use IO::Socket::INET; my $sock1 = IO::Socket::INET->new( PeerAddr => '127.0.0.1', PeerPort => 7777, LocalPort => 6666, ReuseAddr => 1, ) or die $!; my $ah1; $ah1 = AnyEvent::Handle->new( fh => $sock1, on_read => \&send_echo ); my $sock2 = IO::Socket::INET->new( PeerAddr => '127.0.0.1', PeerPort => 7778, LocalPort => 6666, ReuseAddr => 1, ) or die $!; my $ah2; $ah2 = AnyEvent::Handle->new( fh => $sock2, on_read => \&send_echo ); AE::cv->recv; sub send_echo { my $handle = shift; $handle->push_write( substr $handle->{rbuf}, 0, length $handle->{r +buf}, '' ); }

        PS: perhaps you should show us example that doesn't work for you

Re: Asynchronous HTTP Request using specific ports
by andal (Hermit) on Apr 05, 2013 at 07:42 UTC

    The TIME_WAIT state is a protection measure of TCP protocol. If you initiate closure of some TCP connection, then after connection is closed the port used by the connection shall stay open for a while. This is necessary for proper functioning of the TCP.

    There's a way to force the system to cancel TIME_WAIT using SO_REUSEADDR, but you are running chance of confusing your own application. While used for server application, this is considered to be of low probability. But you are doing it for client application that connects to the same set of hosts, so you may get unexpected closures from remote server.

Re: Asynchronous HTTP Request using specific ports
by Anonymous Monk on Apr 04, 2013 at 11:16 UTC
    This seems inconsistent:
    PeerPort => "http(80)", LocalPort => $port, Proto => 'tcp',
    Try (guess) "http($port)"

      It's not inconsistent, cause I want that my destination port will be 80 and my source port will be $port