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


In reply to Problem with non-blocking sockets example from Cookbook - send() vs EWOULDBLOCK by macdee_2

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.