Как правильно обрабатывать частичную запись в сокетах Ruby?

#ruby #sockets

#ruby #сокеты

Вопрос:

Сокеты TCP — это потоки, а не сообщения, поэтому send() функция сокетов berkeley в некоторых системах может отправлять меньше данных, чем требуется. Поскольку сокет Ruby является очень тонкой оболочкой для сокетов berkeley, AFAIK Socket#send будет вести себя точно так же, как сокеты berkeley send() . Итак, каков правильный способ отправки полного сообщения через TCP-сокеты Ruby? В python для этого вызывается специальная функция sendall() . Но в Ruby мне нужно вручную писать такой код:

 while (sent = sock.send( data, 0 ) < data.length do
  data = data[ sent..-1 ]
end
 

Ответ №1:

Чтобы расширить то, что говорит danielnegri, IO.write заканчивается вызовом io_binwrite io.c

Соответствующий бит исходного кода ruby приведен ниже ( n и len изначально устанавливается на длину ваших данных и offset на 0)

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

if (fptr->write_lock) {
  r = rb_mutex_synchronize(fptr->write_lock, io_binwrite_string, (VALUE)amp;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;
 

Как вы можете видеть, пока он не записал все данные, он будет продолжать делать goto retry и пытаться снова. rb_io_wait_writable в основном проверяет, что значение errno таково, что следует повторить попытку (в отличие от чего-то более фатального), а затем вызывает select , чтобы избежать ожидания занятости.

Комментарии:

1. Большое спасибо, именно то, что мне нужно.

Ответ №2:

Недавно я использовал метод ‘write’:

 require "socket"  
server = TCPServer.new('localhost', 20000)  
loop do  
  Thread.start(server.accept) do |s|  
    print(s, " is acceptedn")  
    server.write(Time.now)  
    print(server, " is gonen")  
    server.close  
  end  
end
 

Попробуйте:

 require 'socket'
sock = Socket.open(Socket::PF_INET,Socket::SOCK_STREAM,Socket::IPPROTO_TCP)
@data = "anyThing"
@addr = pack_sockaddr_in(port, host)
sock.connect(@addr)    #make the connection
sock.send(@data, 0)
 

Вы также можете попробовать использовать класс TCPSocket . Я не использовал ни один из этих кодов Ruby, поэтому я не привык к этой конкретной библиотеке; пожалуйста, дайте мне знать, если я все понял неправильно.

 require 'socket'
sock = TCPSocket.new(host, port)
@data = "anyThing"
sock.send(@data, 0) 
 

Комментарии:

1. Есть ли какие-либо доказательства того, что write обрабатывает частичную отправку?