7

サーバーに接続してデータを待機するクライアント Ruby ライブラリを構築していますが、ユーザーがメソッドを呼び出してデータを送信することもできます。

私が使用するメカニズムは、次のようにソケット ペアを初期化するクラスを持つことです。

def initialize
  @pipe_r, @pipe_w = Socket.pair(:UNIX, :STREAM, 0)
end

サーバーにデータを送信するために開発者が呼び出せるようにするメソッドは次のようになります。

def send(data)
  @pipe_w.write(data)
  @pipe_w.flush
end

socket次に、サーバーに接続された と から選択する別のスレッドにループがあり@pipe_rます。

def socket_loop
  Thread.new do
    socket = TCPSocket.new(host, port)

    loop do
      ready = IO.select([socket, @pipe_r])

      if ready[0].include?(@pipe_r)
        data_to_send = @pipe_r.read_nonblock(1024)
        socket.write(data_to_send)
      end

      if ready[0].include?(socket)
        data_received = socket.read_nonblock(1024)
        h2 << data_received
        break if socket.nil? || socket.closed? || socket.eof?
      end
    end
  end
end

これは美しく機能しますが、例のように法線でのみ機能TCPSocketします。IO.select docsOpenSSL::SSL::SSLSocketに従って、代わりにを使用する必要があります。

IO.select を使用する最善の方法は、read_nonblock、write_nonblock などのノンブロッキング メソッドの後に呼び出すことです。

[...]

特に、ノンブロッキング メソッドと IO.select の組み合わせは、OpenSSL::SSL::SSLSocket などの IO のようなオブジェクトに適しています。

これによると、ノンブロッキング メソッドのIO.select に呼び出す必要がありますが、ループでは以前に使用しているため、2 つの異なる IO オブジェクトから選択できます。

IO.selectSSL ソケットでの使用方法の例は次のとおりです。

begin
  result = socket.read_nonblock(1024)
rescue IO::WaitReadable
  IO.select([socket])
  retry
rescue IO::WaitWritable
  IO.select(nil, [socket])
  retry
end

ただし、これは が単一のIO オブジェクトIO.selectで使用されている場合にのみ機能します。

@pipe_r私の質問は、とsocketオブジェクトの両方から選択する必要がある場合、前の例を SSL ソケットで機能させるにはどうすればよいですか?

編集: @steffen-ullrich が提案したことを試しましたが、役に立ちませんでした。以下を使用して、テストに合格することができました。

loop do

  begin
    data_to_send = @pipe_r.read_nonblock(1024)
    socket.write(data_to_send)
  rescue IO::WaitReadable, IO::WaitWritable
  end

  begin
    data_received = socket.read_nonblock(1024)
    h2 << data_received
    break if socket.nil? || socket.closed? || socket.eof?

  rescue IO::WaitReadable
    IO.select([socket, @pipe_r])

  rescue IO::WaitWritable
    IO.select([@pipe_r], [socket])

  end
end

これはそれほど悪くはありませんが、どんな入力も歓迎します。

4

1 に答える 1