2013-03-29 9 views
5

Próbuję więc zasymulować podstawowe połączenia HTTP przy użyciu gniazd i Ruby - dla klasy uczelnianej.Jak prawidłowo obsłużyć trwałe połączenia z gniazdami TCP (aby zasymulować serwer HTTP)?

Chodzi o to, aby zbudować serwer - zdolny do obsługi wielu klientów - który odbiera ścieżkę do pliku i oddaje zawartość pliku - podobnie jak HTTP GET.

Bieżąca pętla implementacji serwera nasłuchuje klientów, uruchamia nowy wątek, gdy nadchodzi połączenie i odczytuje ścieżki plików z tego gniazda. To bardzo głupie, ale działa dobrze podczas pracy z nieistniejącymi połączeniami - jedna prośba na połączenie.

Ale one powinny być wytrwałe.

Co oznacza, że ​​klient nie powinien martwić się zamknięciem połączenia. W wersji nietrwałej serwery powtarzają odpowiedź i zamykają połączenie - Do widzenia klient, pożegnanie. Ale bycie trwałym oznacza, że ​​wątek serwera powinien zapętlać się i czekać na więcej przychodzących żądań, aż ... dopóki nie będzie więcej żądań. W jaki sposób serwer to wie? Nie ma! Potrzebny jest pewien czas oczekiwania. Próbowałem to zrobić przy pomocy limitu czasu Rubiego, ale to nie zadziałało.

Googling dla niektórych rozwiązań - poza tym, że dokładnie unikano używania modułu Timeout - widziałem wiele postów dotyczących metody IO.select, która powinna obsłużyć problem oczekujących na gniazdo lepiej niż przy użyciu wątków i innych rzeczy (które naprawdę brzmi świetnie, biorąc pod uwagę, jak działają wątki Ruby (nie). Próbuję zrozumieć, jak działa IO.select, ale nadal nie udało mi się go uruchomić w bieżącym scenariuszu.

Więc aske zasadzie dwie rzeczy:

  • jak mogę pracować wydajniej ten limit czasu emisji na stronie serwera, albo za jakieś rozwiązanie oparte wątek, opcje gniazd niskiego poziomu lub trochę IO.select magii ?

  • W jaki sposób strona klienta może wiedzieć, że serwer zamknął swoją stronę połączenia?

Oto aktualny kod na serwerze:

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 

Aktualizacja

byłem w stanie symulować zachowanie limitu czasu za pomocą metody IO.select, ale realizacja nie dobrze się czuć, łącząc się z kilkoma wątkami, aby zaakceptować nowe połączenia, i kolejną parą do obsługi zgłoszeń. Współbieżność sprawia, że ​​sytuacja jest szalona i niestabilna, i prawdopodobnie nie będę się z nią trzymał, jeśli nie znajdę lepszego sposobu na wykorzystanie tego rozwiązania.

Aktualizacja 2

Wygląda Timeout jest nadal najlepszym sposobem, aby sobie z tym poradzić. Trzymam się tego, dopóki nie znajdę lepszej opcji. Nadal nie wiem, jak radzić sobie z połączeniami klienta zombie.

Rozwiązanie

I endend przy użyciu IO.wybierz (zainspirował się, patrząc na kod Webricka). Musisz sprawdzić ostateczną wersję: here (lib/http/server/client_handler.rb)

+0

Czy nie można po prostu zamknąć połączenia u klienta po odebraniu wszystkich jego plików i nie ma już żadnych żądań? –

+0

, które mogą Ci pomóc http://stackoverflow.com/questions/6158228/how-do-i-create-persistant-tcpsockets – toch

+0

@MartinJames Dzięki temu proces byłby o wiele łatwiejszy, ale specyfikacja HTTP stwierdza, że ​​klient nie powinien " t martwić się o połączenie; to jest odpowiedzialność serwera. –

Odpowiedz

0

Powinieneś zaimplementować coś w rodzaju pakietów pulsu. Strona klienta powinna wysłać specjalne pakiety po kilku sekundach/minutach, aby upewnić się, że serwer nie ma czasu poł ± czenia po stronie klienta. Unikaj robienia czegokolwiek w tym połączeniu.

Powiązane problemy