Ruby TCPSocket не замечает этого, когда сервер убит - PullRequest
4 голосов
/ 27 марта 2010

У меня есть этот код ruby, который подключается к серверу TCP (а именно, netcat).Он зацикливается 20 раз и отправляет «ABCD».Если я убью netcat, потребуется две итерации цикла, чтобы сработало исключение.В первом цикле после уничтожения netcat никакое исключение не вызывается, и «send» сообщает, что 5 байтов были правильно записаны ... Что в итоге неверно, поскольку, конечно, сервер их никогда не получал.

Есть ли способ обойти эту проблему?Прямо сейчас я теряю данные: поскольку я думаю, что они были правильно переданы, я не воспроизводю их.

#!/usr/bin/env ruby
require 'rubygems'
require 'socket'

sock = TCPSocket.new('192.168.0.10', 5443)
sock.sync = true

20.times do
  sleep 2
  begin
    count = sock.write("ABCD ")
    puts "Wrote #{count} bytes"
  rescue Exception => myException
    puts "Exception rescued : #{myException}"
  end
end

Ответы [ 3 ]

3 голосов
/ 19 февраля 2012

Когда вы отправляете данные, ваш блокирующий вызов вернется, когда данные будут записаны в выходной буфер TCP. Он блокируется, только если буфер заполнен, ожидая, пока сервер подтвердит получение предыдущих данных, которые были отправлены.

Когда эти данные находятся в буфере, сетевые драйверы пытаются отправить данные. Если соединение потеряно, при второй попытке записи ваше приложение обнаруживает нарушенное состояние соединения.

Кроме того, как закрывается соединение? Сервер активно закрывает соединение? В этом случае клиентский сокет будет уведомлен при следующем вызове сокета. Или он разбился? Или, возможно, произошел сбой в сети, который означает, что вы больше не можете общаться.

Обнаружение разорванного соединения происходит только при попытке отправить или получить данные через сокет. Это отличается от активного закрытия соединения. Вы просто не можете определить, живо ли соединение, не делая с ним что-либо.

Так что попробуйте сделать sock.recv(0) после записи - если сокет потерпел неудачу, это вызовет "Errno::ECONNRESET: Connection reset by peer - recvfrom(2)". Вы также можете попробовать sock.sendmsg "", 0 (не sock.write или sock.send), и это выдаст «Errno::EPIPE: Broken pipe - sendmsg(2)».

Даже если вы получили в свои руки пакеты TCP и получили подтверждение того, что данные были получены на другом конце, все еще нет гарантии, что сервер обработает эти данные - они могут находиться во входном буфере, но еще не обработаны .

Все это может помочь определить ранее разорванное соединение, но все равно не гарантирует, что данные были получены и обработаны сервером. Единственный надежный способ узнать, что приложение обработало ваше сообщение, - получить ответ на уровне приложения.

1 голос
/ 28 марта 2010

Чтобы обнаружить изящное закрытие, вам нужно прочитать из сокета - чтение, возвращающее 0, указывает, что сокет закрыт.

Если вам нужно знать, были ли данные успешно отправлены, нет другого способа, кроме реализации ACK для данных на уровне приложения.

1 голос
/ 28 марта 2010

Я пытался без функции sleep (просто чтобы убедиться, что она ничего не удерживала), но все равно не повезло:

#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
require 'activesupport' # Fixnum.seconds

sock = TCPSocket.new('127.0.0.1', 5443)
sock.sync = true

will_restart_at = Time.now + 2.seconds
should_continue = true

while should_continue
  if will_restart_at <= Time.now
    will_restart_at = Time.now + 2.seconds
    begin
      count = sock.write("ABCD ")
      puts "Wrote #{count} bytes"
    rescue Exception => myException
      puts "Exception rescued : #{myException}"
      should_continue = false
    end
  end
end

Я проанализировал с помощью Wireshark, и оба решения в точности ведут себя одинаково.

Я думаю (и не могу быть уверен), что до тех пор, пока вы на самом деле не вызовете your_socket.write (что не произойдет, поскольку сокет все еще открыт, потому что вы не проверяли его возможное уничтожение), сокет не будет подниматься любая ошибка.

Я пытался смоделировать это с помощью nginx и ручных TCP-сокетов. И посмотрите на это:

irb> sock = TCPSocket.new('127.0.0.1', 80)
=> #<TCPSocket:0xb743b824>
irb> sock.write("salut")
=> 5
irb> sock.read
=> "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n</body>\r\n</html>\r\n"

# Here, I kill nginx

irb> sock.write("salut")
=> 5
irb> sock.read
=> ""
irb> sock.write("salut")
Errno::EPIPE: Broken pipe

Так какой вывод отсюда? Если вы на самом деле не ожидаете каких-либо данных от сервера, вы не можете обнаружить, что потеряли соединение:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...