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)
Czy nie można po prostu zamknąć połączenia u klienta po odebraniu wszystkich jego plików i nie ma już żadnych żądań? –
, które mogą Ci pomóc http://stackoverflow.com/questions/6158228/how-do-i-create-persistant-tcpsockets – toch
@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. –