2013-03-29 46 views
5

因此,我試圖用套接字和Ruby來模擬一些基本的HTTP持久連接 - 用於大學課程。如何正確處理持續的TCP套接字連接(模擬HTTP服務器)?

關鍵是要構建一個服務器 - 能夠處理多個客戶端 - 接收文件路徑並返回文件內容 - 就像HTTP GET一樣。

當前服務器實現循環偵聽客戶端,當有傳入連接時觸發新線程並從此套接字讀取文件路徑。這非常愚蠢,但在使用非連續連接時工作正常 - 每個連接一個請求。

但他們應該持久。

這意味着客戶端不應該擔心關閉連接。在非持久性版本中,服務器迴應響應並關閉連接 - 告別客戶端。 但是持久性意味着服務器線程應該循環並等待更多的傳入請求,直到...直到沒有更多的請求。服務器如何知道這一點?它不!某種超時是需要的。我試圖用Ruby的Timeout來做到這一點,但沒有奏效。

谷歌搜索的一些解決方案 - 除了被徹底建議避免使用超時模塊 - 我見過很多關於IO.select方法的帖子,應該處理這個套接字等待問題的方式比使用線程和東西更好真的聽起來很酷,考慮Ruby線程(不)工作)。我試圖在這裏理解IO.select的工作方式,但仍然無法使其在當前場景中工作。

所以我aske基本上是兩件事情:

  • 我怎樣纔能有效地工作超時問題在服務器端,或者使用一些基於線程的解決方案,低級別的套接字選項或一些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

好像超時仍是處理的最佳方式。我堅持下去,直到找到更好的選擇。 我仍然不知道如何處理殭屍客戶端連接。

解決方案

我endend了使用IO。選擇(在查看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

你應該實現類似心跳包的東西。客戶端應該在幾秒/分鐘後發送特殊的包,以確保服務器沒有時間在客戶端連接。你只是在這個電話中避免做任何事情。

相關問題