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

I've been using the code from Recipe 17.13 in the Perl Cookbook (Non-Forking Servers) in one of my own applications.

This uses Non-blocking I/O to read and write sockets. I've included the code I believe to be incorrect below. Note: I've taken this code from IO::NonBlocking on CPAN, to ensure it's freely available. IO::NonBlocking is heavily based on the cookbook recipe.

My problem is that if you try to write a large amount of data at once to a socket, the code will incorrectly disconnect the client. On examination of $! and $rv, it would appear that send() managed to send some, but not all, of the data (ie $rv < length $outbuffer{$client}), but $! is not set. This is contrary to what the code below expects - it would seem to expect that if not all of the data was sent, then $! would be set to EWOULDBLOCK, and $rv would be set to the amount sent.

However, $! is closely tied to the errno() system call - ie it's an error. If you examind the man page for send(2), it suggests that errno() is only set if it's an Error condition - ie send() call returns -1. This equates to $rv being undefined in Perl code.

I infer that a partial send is not an error condition, and will not set errno() at all. I've checked this to the best of my ability using the Linux send(2) man page, and Unix Network Programming (1st edition was all I can lay my hands on).

# Buffers to flush? foreach $client ($select->can_write(1)) { # Skip this client if we have nothing to say next unless exists $outbuffer{$client}; eval{ $rv = $client->send($outbuffer{$client}, 0); }; push(@bad_client,$client),next if $@; unless (defined $rv) { # Whine, but move on. warn "I was told I could write, but I can't.\n"; next; } if ( $rv == length $outbuffer{$client} || $! == POSIX::EWOULDBLOCK) { substr($outbuffer{$client}, 0, $rv) = ''; delete $outbuffer{$client} unless length $outbuffer{$client}; } else { # Couldn't write all the data, and it wasn't because # it would have blocked. Shutdown and move on. $self->disconnect_client($client); next; } }
Below here, I have posted my new code. This separates a successful send (partial or not), from an EWOULDBLOCK condition, and any other error conditions.
# Buffers to flush? foreach $client ($select->can_write(1)) { # Skip this client if we have nothing to say next unless exists $outbuffer{$client}; $rv = send($client, $outbuffer{$client}, 0); if ($! == POSIX::EWOULDBLOCK) { #caught a EWOULDBLOCK. Ignore. } elsif (! defined $rv) { # Whine, but move on. warn "I was told I could write, but I can't : $!\n"; next; } elsif ($rv) { substr($outbuffer{$client}, 0, $rv) = ''; warn "successfully wrote $rv bytes\n" if length $outbuffer{$client}; delete $outbuffer{$client} unless length $outbuffer{$client}; } else { # Couldn't write all the data, and it wasn't because # it would have blocked. Shutdown and move on. warn "Failed to write all data, \$rv was $rv, \$! was ".($!+0).", +length was ".(length $outbuffer{$client})."\n"; $self->disconnect_client($client); next; } }

I'd be obliged if any Monks more knowledgable in these matters than me could comment on my findings, and if possible test them. For a successful *partial* send to occur, I've discovered that you need to send about a 13 kilobyte string over an Ethernet connection (not localhost loopback). I've also been using a string with no new lines in it, but that's probably not a requirement.

Many thanks

Macdee

Replies are listed 'Best First'.
Re: Problem with non-blocking sockets example from Cookbook - send() vs EWOULDBLOCK
by Anonymous Monk on Jan 18, 2004 at 15:32 UTC
    My problem is that if you try to write a large amount of data at once to a socket, the code will incorrectly disconnect the client.
    patient: Doctor, doctor, it hurts when I poke it, what do I do?!
    doctor: So don't poke it.

    In other words, code around it. Who wrote the client? You need to figure out why the client is not reading all of the data.

      I wrote the client. It originally used my $str = <$socket> etc. When problems started developing, I rewrote using sysread, syswrite, to no avail. Moreover, I can have the same problem occur using Linux's telnet as a client. Are you going to tell me that's at fault?

      I agree that perhaps sending so much data at once is an unusual, but it should not be exceptional.

      Where I come from, it's better to fix the problem rather than the symptoms.

      Macdee

Re: Problem with non-blocking sockets example from Cookbook - send() vs EWOULDBLOCK
by gellyfish (Monsignor) on Jan 19, 2004 at 13:32 UTC

    You might be falling foul of a restriction in the transport as described in the send(2) manpage:

    If the message is too long to pass atomically through the underlying protocol, the error EMSGSIZE is returned, and the message is not transmitted.
    You might try seeing what value $! is set to in the case of your failure.

    /J\

      $! is not set at all, either in string form ("$!") or numerical form. The return value is set to the length that did send (not all of it).

      I'm asking if the return value from send(2) will always be -1 if Errno is set, or are there cases where an Errno can be set, yet some data still sent. I don't think there is, yet the Cookbook code suggests that EWOULDBLOCK is such a case. My testing shows this to be incorrect (at least on Linux).

      If I generates a script that provided the example case, would people test it on their platform?

      Macdee