Мне нужно пересылать данные между двумя файловыми дескрипторами (сокет, канал, что угодно), пока один из концов не умрет. Когда входной конец умирает, выберите сообщает FD как читаемый и выдает ошибку при чтении, которая завершает l oop. Когда выходной конец записи умирает, я не получаю никакого полезного результата от select () и write ().
Во-первых, я даже не выбираю () выходной конец для записи, если у меня нет ожидающих данных. Он почти всегда готов к приему данных, поэтому я бы просто занялся l oop в противном случае.
Даже добавив тайм-аут для select (), чтобы он выходил из строя, даже когда ничего явно не нужно делать, и записывал пустую строку на выходе не дает ошибок. Приложение просто зависает до тех пор, пока на вход не поступит больше данных, которые затем инициируют непустую запись и возвращают ошибку.
Если выходной конец подключен к процессу через канал, я могу обнаружить, что процесс завершился. Однако, когда другой конец - сервер, я не могу сказать, что соединение должно быть завершено. Я полагаю, что я могу изменить протокол сервера, чтобы при прохождении соединения аккуратно закрывалось сообщение о прощании, чтобы зависание происходило только после того, как сервер просто умирает.
def stream_from other
begin
stream_desc = "#{other.desc} (#{other.fileno}) -> #@desc (#{fileno})"
STDERR.puts "Starting iothread " + stream_desc
buffer = []
readerr = nil
while true do
begin
r, w, e = IO.select [other.io], (buffer[0] ? [@io] : []) , [other.io,@io], 1
STDERR.puts "Select #{stream_desc} b:#{buffer.inspect} r:#{r.inspect} w:#{w.inspect} e:#{e.inspect}"
rescue Errno::EBADF
fd = @io.fileno rescue nil
raise PipeError.new "#{desc}: #{$!.message}" if ! fd
readerr = $!
end
begin
if r && r[0] then
data = r[0].read_nonblock BLOCKSIZE
if data then
STDERR.puts "Read from #{other.desc} (#{other.fileno}) '#{data}'"
buffer.push data
end
end
rescue Errno::ECONNRESET, EOFError, IOError, Errno::EPIPE, Errno::EBADF
readerr = $!
end
begin
@io.write ""
while buffer[0] do
data = buffer.shift
written = @io.write_nonblock data
STDERR.puts "Written to #{@desc} (#{fileno}) '#{data[0...written]}'"
if written < data.length then
buffer.unshift data[written..-1]
break
end
break if ! buffer[0]
end
rescue Errno::ECONNRESET, EOFError, IOError, Errno::EPIPE, Errno::EBADF
raise PipeError.new "#{desc}: #{$!.message}"
end
raise PipeError.new "#{other.desc}: #{readerr.message}" if readerr
end
ensure
STDERR.puts "Closing iothread " + stream_desc
other.cleanup
cleanup
end
end