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).
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}; 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; } }
# 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
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |