2013-03-29 16 views
5

私はソケットとRubyを使っていくつかの基本的なHTTP永続接続をシミュレートしようとしています。永続的なTCPソケット接続(HTTPサーバーをシミュレートするため)を正しく処理するにはどうしたらいいですか?

重要なのは、HTTP GETのように、ファイルパスを受け取りファイルコンテンツを返す複数のクライアントを処理できるサーバーを構築することです。

現在のサーバー実装はクライアントのリスニングをループし、接続が着信すると新しいスレッドを起動し、このソケットからファイルパスを読み取ります。それは非常に愚かですが、非常駐接続で作業する場合はうまく動作します - 接続ごとに1つの要求。

しかし、それらは永続的でなければなりません。

これは、クライアントが接続を閉じるのを心配する必要がないことを意味します。非永続バージョンでは、サーバーは応答をエコーし​​、接続を閉じます。さよならクライアント、別れ。 しかし、永続的であるということは、サーバスレッドはループして、より多くの要求が来るまで待つ必要があることを意味します。サーバーはそれをどのように知っていますか?それはありません!何らかの種類のタイムアウトが必要です。私はRubyのタイムアウトでそれをしようとしましたが、うまくいきませんでした。

いくつかのソリューションのためのグーグル - タイムアウトモジュールの使用を避けるように徹底的に勧告されています - 私はIO.selectメソッドに関する多くの記事を見てきました。 Rubyスレッド(動作しない方法)を考慮して、本当にクールに聞こえます。私はここでIO.selectの動作を理解しようとしていますが、現在のシナリオでは動作させることができませんでした。

  • 私は効率的にどちらかのいくつかのスレッドベースのソリューション、低レベルのソケットオプションまたは一部IO.selectの魔法を使用して、サーバ側でこのタイムアウトの問題を働かせることができる方法:

    は、だから私は基本的に二つのことアスケ?

  • クライアント側は、サーバーが接続側を閉じていることをどのように知ることができますか?

ここでは、サーバの現在のコードです:

require 'date' 
module Sockettp 
    class Server 
    def initialize(dir, port = Sockettp::DEFAULT_PORT) 
     @dir = dir 
     @port = port 
    end 

    def start 
     puts "Starting Sockettp server..." 
     puts "Serving #{@dir.yellow} on port #{@port.to_s.green}" 

     Socket.tcp_server_loop(@port) do |socket, client_addrinfo| 
     handle socket, client_addrinfo 
     end 
    end 

    private 
    def handle(socket, addrinfo) 
     Thread.new(socket) do |client| 
     log "New client connected" 
     begin 
      loop do 
      if client.eof? 
       puts "#{'-' * 100} end connection" 
       break 
      end 

      input = client.gets.chomp 

      body = content_for(input) 

      response = {} 

      if body 
       response.merge!({ 
       status: 200, 
       body: body 
       }) 
      else 
       response.merge!({ 
       status: 404, 
       body: Sockettp::STATUSES[404] 
       }) 
      end 

      log "#{addrinfo.ip_address} #{input} -- #{response[:status]} #{Sockettp::STATUSES[response[:status]]}".send(response[:status] == 200 ? :green : :red) 

      client.puts(response.to_json) 
      end 
     ensure 
      socket.close 
     end 
     end 
    end 

    def content_for(path) 
     path = File.join(@dir, path) 

     return File.read(path) if File.file?(path) 
     return Dir["#{path}/*"] if File.directory?(path) 
    end 

    def log(msg) 
     puts "#{Thread.current} -- #{DateTime.now.to_s} -- #{msg}" 
    end 
    end 
end 

が更新

私はIO.selectメソッドを使用して、タイムアウトの挙動をシミュレートすることができましたが、実装はしていません新しい接続を受け入れるための2つのスレッドとリクエストを処理するためのもう1つのカップルを組み合わせると良い気分になります。同時実行性は状況を狂わせ、不安定にしますが、私はこの解決策を使用するより良い方法を見つけ出すことができない限り、おそらくそれに固執していません。

アップデート2

タイムアウトはまだこれを処理するための最良の方法であるように思えます。私はそれが良いオプションを見つけるまでそれに固執しています。 私はまだゾンビのクライアント接続に対処する方法がわかりません。私はIOを使ってendend

ソリューション

。select(webrickコードを見ているときに触発されました)。 here(lib/http/server/client_handler.rb)

+0

クライアントですべてのファイルを受信して​​も、それ以上の要求がない場合、クライアントで接続を閉じることはできませんか? –

+0

これはあなたを助けることができますhttp://stackoverflow.com/questions/6158228/how-do-i-create-persistant-tcpsockets – toch

+0

@MartinJamesそれはプロセスをはるかに簡単にするだろうが、HTTP仕様では、接続について心配しないでください。これはサーバーの責任です。 –

答えて

0

ハートビートパケットのようなものを実装する必要があります。クライアント側は、数秒後に特別なパケットを送信して、クライアント側の接続を外してください。この呼び出しでは何もしないでください。

関連する問題