Как правильно обрабатывать частичную запись в Ruby Sockets? - PullRequest
3 голосов
/ 26 октября 2011

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

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

Ответы [ 2 ]

2 голосов
/ 04 января 2012

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

Соответствующий бит источника рубина находится ниже (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)&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, чтобы избежать ожидания занятости.

0 голосов
/ 04 января 2012

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

require "socket"  
server = TCPServer.new('localhost', 20000)  
loop do  
  Thread.start(server.accept) do |s|  
    print(s, " is accepted\n")  
    server.write(Time.now)  
    print(server, " is gone\n")  
    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) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...