Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly handle partial write in Ruby Sockets?

Tags:

ruby

sockets

TCP sockets are streams, not messages, so berkeley sockets send() function on some systems may send less data than required. Since Ruby Socket is very thin wrapper over berkeley sockets, AFAIK Socket#send will behave exactly like berkeley sockets send(). So what is the correct way to send a complete message via Ruby TCP sockets? In python it's a special function for that called sendall(). But in Ruby i need to manually write code like that:

while (sent = sock.send( data, 0 ) < data.length do
  data = data[ sent..-1 ]
end
like image 700
grigoryvp Avatar asked Aug 02 '26 15:08

grigoryvp


1 Answers

To expand on what danielnegri says, IO.write ends up calling io_binwrite in io.c

The relevant bit of the ruby source is below (n and len are initially set to the length of your data and offset to 0)

retry:
arg.offset = offset; 
arg.length = n;

if (fptr->write_lock) {
  r = rb_mutex_synchronize(fptr->write_lock, io_binwrite_string, (VALUE)&arg);
}
else {
  long l = io_writable_length(fptr, n);
  r = rb_write_internal(fptr->fd, ptr+offset, l);
}
if (r == n) return len;
if (0 <= r) {
  offset += r;
  n -= r;
  errno = EAGAIN;
}
if (rb_io_wait_writable(fptr->fd)) {
  rb_io_check_closed(fptr);
  if (offset < RSTRING_LEN(str))
    goto retry;
}
return -1L;

As you can see, until it has written all the data it will keep on doing goto retry and trying again. rb_io_wait_writable basically checks that the value of errno is such that one should try again (as opposed to something more fatal) and then calls select to avoid busy-waiting.

like image 155
Frederick Cheung Avatar answered Aug 05 '26 10:08

Frederick Cheung



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!