Piszę serwer WWW Sinatra, że chciałbym być REST, ale chodzi o to, że musi on wchodzić w interakcje z innym serwerem, który komunikuje się wyłącznie za pośrednictwem gniazd internetowych. Tak, to musi się zdarzyć:Sinatra używająca klienta websocket do odpowiedzi na żądanie http
- Żądanie przychodzi do mojego serwera Sinatra od klienta
- Serwer otwiera websocket na serwerze zagranicznym
- Serwer asynchronicznie czeka na wiadomości i rzeczy z zagranicznych serwer aż gniazdo jest zamknięte (to powinno brać tylko dwieście lub tak milisekund)
- Mój serwer wysyła odpowiedź do klienta
Jestem pewien, że nie jest to zbyt skomplikowane do wykonania, ale Trochę mi to utknęło. Zasadniczo, jeśli cała logika gniazda sieciowego może być opakowana w jedną funkcję, to można by zablokować tę funkcję, i to by było właśnie to. Ale nie wiem, jak owijać logikę gniazda sieciowego i blokować ją. Co myślisz? Uproszczona wersja tego, co mam, znajduje się poniżej.
require 'sinatra'
require 'websocket-client-simple'
get '/' do
ws = WebSocket::Client::Simple.connect(' ws://URL... ')
ws.on :message do
puts 'bar'
end
ws.on :close do
# At this point we need to send an HTTP response back to the client. But how?
end
ws.on :open do
ws.send 'foo'
end
end
EDIT
Po głębszej refleksji, zdałem sobie sprawę, że sposób, że może to być zrobione za pomocą gwintu i halt wybudzenia gwintu. To czuje się dość skomplikowany i nie jestem pewien jak to zrobić poprawnie z Ruby, ale jest to pomysł:
require 'sinatra'
require 'websocket-client-simple'
get '/' do
socketResponse('wss:// ... URL ...')
'Got a response from the web socket server!'
end
def socketResponse(url)
thread = Thread.new do
ws = WebSocket::Client::Simple.connect(url)
ws.on :message do
puts 'bar'
# Maybe store each response in a thread-safe array to retrieve later or something
end
ws.on :close do
thread.run
end
ws.on :open do
ws.send 'foo'
end
Thread.stop
end
end
EDIT 2
jakie poczyniła dalsze postępy. Używam teraz klejnotu Async Sinatra, który wymaga serwera WWW Thin. Oto, jak jest skonfigurowany:
require 'sinatra'
require 'sinatra/async'
require 'websocket-client-simple'
set :server, 'thin'
register Sinatra::Async
aget '/' do
puts 'Request received'
socketResponse('wss:// ... URL ...')
end
def socketResponse(url)
ws = WebSocket::Client::Simple.connect(url)
puts 'Connected to web socket'
ws.on :message do |message|
puts 'Got message:' + message.to_s
end
ws.on :close do
puts 'WS closed'
body 'Closed ...'
end
ws.on :open do
puts 'WS open'
message = 'A nice message to process'
ws.send message
puts 'Sent: ' + message
end
end
Chodzi o to, że nadal nie działa. Wyjście konsoli jest zgodne z oczekiwaniami:
Request received
Connected to web socket
WS open
Sent: A nice message to process
Got message: blah blah blah
WS closed
Ale nie wysyła żadnych danych z powrotem do klienta. Wydaje się, że metoda body 'Closed ...'
nie przynosi żadnego efektu.
Sidenote: projekt sugerowany w pytaniu jest nieproduktywny, a wydajność ciężka. Bardziej sensowne jest utrzymywanie otwartego połączenia przez cały czas działania aplikacji. Taki jest cały cel websockets - utrzymywanie trwałego połączenia. – Myst
To nie jest mój prawdziwy kod, ale tylko najprostszy sposób jego napisania, który mógłbym wymyślić, aby zademonstrować problem. Ale dziękuję, to dobra wskazówka. – tschwab
Aby wyjaśnić, nie chcesz, aby strona ładowała się po uruchomieniu kodu gniazda sieciowego? Podajesz również, że powinien być asynchroniczny, więc jestem zdezorientowany. Możesz mieć funkcję asynchroniczną wewnątrz trasy, ponieważ trasa powróci bez żadnych informacji z metody asynchronicznej. – Cereal