ruby - プログラミング - so_sndtimeo




SO_RCVTIMEOソケットオプションを使用してRubyでソケットタイムアウトを設定する (2)

RubyのIOクラスからselectを使って効率的に行うことができます。

IO::selectは4つのパラメータが必要です。 最初の3つは監視するソケットの配列で、最後はタイムアウト(秒単位で指定)です。

selectの仕組みは、IOオブジェクトのリストを、指定された操作のために準備が整った状態にすることです。少なくとも1人が読み込み、書き込み、またはエラーを発生させる準備ができるまでブロックします。

したがって、最初の3つの引数は、監視するさまざまなタイプの状態に対応しています。

  • 読書の準備ができている
  • 書く準備ができている
  • 保留中の例外があります

4番目は設定したいタイムアウトです(ある場合)。 このパラメータを利用する予定です。

Selectは、監視されている特定のアクションに対してオペレーティングシステムによって準備されているとみなされるIOオブジェクトの配列(この場合はソケット)を含む配列を返します。

したがって、selectの戻り値は次のようになります。

[
  [sockets ready for reading],
  [sockets ready for writing],
  [sockets raising errors]
]

ただし、オプションのタイムアウト値が指定され、IOオブジェクトがタイムアウト秒以内に準備ができていない場合、selectはnilを返します。

したがって、Rubyでパフォーマンスの良いIOタイムアウトを実行し、タイムアウトモジュールを使用する必要がない場合は、次の操作を実行できます。

socketでの読み取りのtimeout秒を待つ例を作成しましょう:

ready = IO.select([socket], nil, nil, timeout)

if ready
  # do the read
else
  # raise something that indicates a timeout
end

これは、(Timeoutモジュールのように)タイムアウトごとに新しいスレッドを回転させないという利点があり、Rubyでは多くのタイムアウトを持つマルチスレッドアプリケーションをはるかに高速化します。

私はSO_RCVTIMEOソケットオプションを介してRubyでソケットタイムアウトを作ろうとしていますが、最近の* nixオペレーティングシステムには何の影響もないようです。

Rubyのタイムアウトモジュールを使用することはオプションではありません。タイムアウトごとにスレッドを生成したり結合したりする必要があり、コストが高くなる可能性があります。 低ソケットタイムアウトを必要とし、スレッド数が多いアプリケーションでは、基本的にパフォーマンスが低下します。 これはStack Overflowを含む多くの場所で注目されています。

私はMike Perhamの優れた記事をhere読んでいます。問題を1つのファイルに減らす努力の中で、実行可能なコードのファイルには、要求を受け取るTCPサーバーの簡単な例が作成され、要求で送信された時間が待っています接続を閉じます。

クライアントはソケットを作成し、受信タイムアウトを1秒に設定してサーバーに接続します。 クライアントは、5秒後にセッションを終了するようサーバーに指示し、データを待機します。

クライアントは1秒後にタイムアウトする必要がありますが、5の後に接続を正常に終了します。

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

def timeout
  sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)

  # Timeout set to 1 second
  timeval = [1, 0].pack("l_2")
  sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval

  # Connect and tell the server to wait 5 seconds
  sock.connect(Socket.pack_sockaddr_in(1234, '127.0.0.1'))
  sock.write("5\n")

  # Wait for data to be sent back
  begin
    result = sock.recvfrom(1024)
    puts "session closed"
  rescue Errno::EAGAIN
    puts "timed out!"
  end
end

Thread.new do
  server = TCPServer.new(nil, 1234)
  while (session = server.accept)
    request = session.gets
    sleep request.to_i
    session.close
  end
end

timeout

私はTCPSocket(自動的に接続する)で同じことをやってみたし、 redisや他のプロジェクトでも同様のコードを見てきました。

さらに、私はgetsockoptように呼び出してオプションが設定されていることを確認できます:

sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect

このソケットオプションを設定することは、実際に誰のためにも有効ですか?


私のテストとJesse Storimerの "TCPソケットを使った作業"(Ruby)の優れた電子ブックによれば、タイムアウトソケットオプション Ruby 1.9 では動作ません (2.0と2.1と仮定します)。 ジェシーは言う:

お使いのオペレーティングシステムには、SNDTIMEOおよびRCVTIMEOソケットオプションを使用して設定できるネイティブソケットタイムアウトもあります。 しかし、Ruby 1.9以降、この機能はもはや機能しません。

ワオ。 ストーリーの道徳は、これらのオプションを忘れて、 IO.selectまたはTony ArcieriのNIOライブラリを使用することだと思います。





tcp-ip