Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

IO::Socket connect fails to block

by mr.nick (Chaplain)
on Jan 06, 2012 at 18:29 UTC ( [id://946643]=perlquestion: print w/replies, xml ) Need Help??

mr.nick has asked for the wisdom of the Perl Monks concerning the following question:

I must be missing something very basic here, but I'm having a problem with connecting to a single threaded server based on IO::Socket. Basically, the following does not block until the other side does a "$server->accept".
my $sock = new IO::Socket::INET( PeerAddr => '127.0.0.1', PeerPort => 12000, Proto => 'tcp', Blocking => 1); print $sock "Hello\n";
I had expected that if the server was written as such
my $server = IO::Socket::INET->new( LocalPort => 12000, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1 ); die $! unless $server; while (my $sock = $server->accept) { print "Connected to sock\n"; print while <$sock>; }
Once it had accepted one connection, any subsequent ones would block until it could accept() again.

I am obviously wrong here.

The above is contrived, of course, but what I want is the part that connects to block until the server is finished with it's current request.

As I said, I'm missing something obvious I'm sure, so please help out a brain-addled fellow monk.

EDIT

Here is the test code I wrote in it's entirety:

use strict; use warnings; use IO::Socket::INET; if ($ARGV[0] == 1) { my $server = IO::Socket::INET->new( LocalPort => 12000, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1 ); die $! unless $server; while (my $sock = $server->accept) { print "Connected to sock\n"; print while <$sock>; } } if ($ARGV[0] == 2) { my $sock = new IO::Socket::INET( PeerAddr => '127.0.0.1', PeerPort => 12000, Proto => 'tcp', Blocking => 1 ); print "sock is $sock\n"; }
Then what I did was this:
$ perl SocketT.pl 1 (to launch as a server) $ telnet localhost 12000 (in another window, to tie up the server) $ perl SocketT.pl 2 (to test if it would connect and print out)

mr.nick ...

Replies are listed 'Best First'.
Re: IO::Socket connect fails to block
by zwon (Abbot) on Jan 07, 2012 at 03:46 UTC

    When client calls IO::Socket::INET->new it sends to server TCP packet with SYN flag in it. Since given TCP port on server is in LISTENING state, server immediately replies with SYN,ACK, it is done by kernel, and it doesn't matter that your application is not blocked in accept(2). After receiving SYN,ACK client side treats connection as ESTABLISHED, at this moment connect(2), and consequently IO::Socket::INET->new return. Also client sends ACK to server, upon receiving this ACK server either ignores it if application listen queue is full, or assumes connection ESTABLISHED and places it into application queue, again it doesn't matter what application is doing at the moment. Application queue size is determined by Listen parameter (though maybe not exactly equal to it, e.g. on BSD derived systems if you set Listen to 0 queue size will be 1), which in your case is quite big. Note, that changing Listen doesn't affect client's behaviour, as this parameter is taken into account only upon receiving ACK from client, at which stage client already thinks it is connected.

    So, your scripts work exactly as I expect.

    P.S.: Given description is valid for modern Linux and may be slightly different on other systems.

    P.P.S.: generally you shouldn't bother if server already accepted connection or not, you can just start using socket returned by IO::Socket::INET->new, if connection is not yet accepted you will be blocked in recv(2) or send(2) at some point later.

      Given description is valid for modern Linux and may be slightly different on other systems.

      I agree. Depending on whatever crappy windows version one uses, blocking sockets may not block or non-blocking sockets may block. And there's a difference between blocking while communicating and blocking while waiting for a new connection.

      For me, it was always a bit confusing when switching between Windows and Linux - or to be more precise developing basic TCP connections anywhere. I "fixed" this PEBCAC problem by switching to HTTP whenever possible. In my case, this usually means HTTP::Server::Simple::CGI or Maplat on the server and something like WWW::Mechanize::GZip on the client.

      Of course, this is only my small, personal development universe, yours may have different requirements and bugs or a even better developer altogether...

      "Believe me, Mike, I calculated the odds of this succeeding against the odds I was doing something incredibly stupid… and I went ahead anyway." (Crow in "MST3K The Movie")
Re: IO::Socket connect fails to block
by mr.nick (Chaplain) on Jan 06, 2012 at 19:56 UTC
    And to comment on my own issue, this test script
    use strict; use warnings; use IO::Socket::INET; sub server { my $server = IO::Socket::INET->new( LocalPort => 12000, Proto => 'tcp', ReuseAddr => 1, Listen => SOMAXCONN ); die $! unless $server; while (my $sock = $server->accept) { print "Connected to $sock\n"; print while <$sock>; } } sub client { my $sock = new IO::Socket::INET( PeerAddr => '127.0.0.1', PeerPort => 12000, Proto => 'tcp', Blocking => 1 ); print $sock "yes\n"; print $sock "no\n"; print "sock is $sock\n"; } fork || server(); fork || client(); fork || client(); sleep 100;
    Works as expected: both "yes" and "no" are correctly outputted even though only one client is connected at a time.

    There must be something in my code that's eating the additional lines of text.

    Sigh. Thanks for letting me talk this out! :)

    mr.nick ...

Re: IO::Socket connect fails to block
by gman (Friar) on Jan 06, 2012 at 19:21 UTC
    Shouldn't reuse be set to null?

    ReuseAddr Set SO_REUSEADDR before binding Reuse Set SO_REUSEADDR before binding (deprecated, prefer ReuseA +ddr)

    Update: I tried you above code and it works as expected, printing out any lines "queued" up from the second client.

      Tried that; no change in behavior. To shed a little light on the actual issue I'm having, consider this scenario:

      1. A single threaded/noforking server is running.
      2. Two clients are constantly connecting to it, sending TWO lines of text each time, then waits for a response.

      What happens is that the first client to connect sends it's two lines, gets it's response, and continues. The second client connects, sends it's two lines, BUT when the server finishes the first client and moves onto the 2nd client, it only sees ONE line of text.

      Here's some output that may or may not help:

      client 1: 2012-01-06-14.32.08 [Collection..:ConnectToDispatch 184] Connecting +to dispatch at 127.0.0.1 .... 2012-01-06-14.32.08 [ CS_Server.pl 175] Consulting +Dispatcher 2012-01-06-14.32.08 [Collection..twork::SendMessage 118] Sending REQ +UEST_TYPE (10) 2012-01-06-14.32.09 [Collection..ork::SendSocketRow 160] Sent socket + row COLLECTION 2012-01-06-14.32.10 [Collection..RecvAndValidateMsg 130] Received me +ssage ACK_REQUEST_TYPE client 2: 2012-01-06-14.32.13 [Collection..:ConnectToDispatch 184] Connecting +to dispatch at 127.0.0.1 .... 2012-01-06-14.32.13 [ CS_Server.pl 175] Consulting +Dispatcher 2012-01-06-14.32.13 [Collection..twork::SendMessage 118] Sending REQ +UEST_TYPE (10) 2012-01-06-14.32.14 [Collection..ork::SendSocketRow 160] Sent socket + row COLLECTION server (first client): 2012-01-06-14.32.06 [Collection..CP::AcceptListener 64] Accepted co +nnection from 127.0.0.1:1866 (IO::Socket::INET=GLOB(0x1033f3d8)) >>> message is '10 ' 2012-01-06-14.32.06 [ Collection::IPC::TCP::Receive 155] Received '1 +0' from IO::Socket::INET=GLOB(0x1033f3d8) >>> message is 'COLLECTION ' 2012-01-06-14.32.07 [ Collection::IPC::TCP::Receive 155] Received 'C +OLLECTION' from IO::Socket::INET=GLOB(0x1033f3d8) 2012-01-06-14.32.07 [Collection..er::ProcessRequest 90] Received re +quest type of 'COLLECTION' 2012-01-06-14.32.07 [ Collection::IPC::TCP::Send 167] Sending '15 +' to IO::Socket::INET=GLOB(0x1033f3d8) server (2nd client): 2012-01-06-14.32.11 [Collection..CP::AcceptListener 64] Accepted co +nnection from 127.0.0.1:1872 (IO::Socket::INET=GLOB(0x103f1278)) >>> message is '10 ' 2012-01-06-14.32.11 [ Collection::IPC::TCP::Receive 155] Received '1 +0' from IO::Socket::INET=GLOB(0x103f1278)
      No "Received 'COLLECTION'" from that point.

      So it appears to me that when a client hasn't been "accept"ed and sends multiple lines of text, only one is being received when it eventually is connected.

      That's my issue. I'll continue to go over my code again and see if I'm not doing something stupid, but honestly this is confusing me.

      mr.nick ...

        Just to comment and provide some closure to this, in case anyone looks, recall that my seeming issue was this:

        1. Client connects to server and does some i/o
        2. 2nd Client attempts to connect to server and do some i/o. It spews a few lines of text at the socket and waits.
        3. Server finishes with client #1, accepts() client #2
        4. Not all the text send over in #2 is there.

        Well, this is under Windows (Cygwin to exact) and I found the issue... during the negotiation between client and server multiple debugging output is done. In the DebugMsg() function is the following:

            chomp(my $d = `date "+%Y-%m-%d-%H.%M.%S"`);

        That is the culprit. Shelling out and running date is somehow flushing/truncating the sockets. A simple change from that to

                my $d = strftime("%Y-%m-%d-%H:%M:%S",localtime(time));

        Completely fixed my issue.

        Pain in the ass, eh?

        mr.nick ...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://946643]
Approved by Eliya
Front-paged by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (7)
As of 2024-04-18 10:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found