Как пересылать данные между FD, пока один из FD не умрет? - PullRequest
0 голосов
/ 23 января 2020

Мне нужно пересылать данные между двумя файловыми дескрипторами (сокет, канал, что угодно), пока один из концов не умрет. Когда входной конец умирает, выберите сообщает 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

...