2017-08-31 1 views
9

Ich schreibe einen Sinatra-Webserver, den ich möchte RESTful sein, aber die Sache ist, dass es mit einem anderen Server interagieren muss, der ausschließlich über Web-Sockets kommuniziert. So muss dies geschehen kann:Sinatra mit einem Websocket-Client, um auf eine HTTP-Anfrage zu antworten

  1. eine Anfrage kommt in meinen Sinatra-Server von einem Client
  2. Mein Server einen Web-Socket an den fremden Server öffnet
  3. Mein Server asynchron wartet auf Nachrichten und Dinge aus der Fremd Server, bis der Socket geschlossen wird (dies sollte nur zweihundert oder so Millisekunden)
  4. Mein Server sendet eine Antwort an den Client zurück

ich bin sicher, dass dies nicht zu kompliziert zu erreichen, aber Ich bin ein bisschen dran. Wenn die gesamte Web-Socket-Logik in eine einzige Funktion eingebunden werden kann, dann könnte diese Funktion blockiert werden, und das wäre es auch. Aber ich weiß nicht, wie man die Web-Socket-Logik einpackt und blockiert. Was denken Sie? Eine vereinfachte Version von dem, was ich habe, ist unten.

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

Nach weiteren Gedanken, erkannte ich, dass eine Art und Weise, dass dies halt mit einem Gewinde getan werden könnte, und einen Thread aufwachen. Das fühlt sich ziemlich aufwendig, und ich bin nicht sicher, wie dies mit Rubin richtig zu tun, aber das ist die Idee:

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

ich weitere Fortschritte gemacht habe. Ich verwende jetzt den Async Sinatra Edelstein, der den Thin Webserver erfordert. Dies ist, wie es eingerichtet ist:

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 

Die Sache ist, es funktioniert immer noch nicht. Seine Konsolenausgabe ist wie erwartet:

Request received 
Connected to web socket 
WS open 
Sent: A nice message to process 
Got message: blah blah blah 
WS closed 

Aber es sendet keine Daten zurück zum Client. Die body 'Closed ...' Methode scheint keine Wirkung zu haben.

+1

Hinweis: Das in Ihrer Frage vorgeschlagene Design ist kontraproduktiv und leistungsschwach. Es ist sinnvoller, die Websocket-Verbindung solange offen zu halten, wie Ihre Anwendung läuft. Das ist der springende Punkt von Websockets - eine dauerhafte Verbindung aufrecht zu erhalten. – Myst

+0

Dies ist nicht mein richtiger Code, sondern nur die einfachste Art, sie zu schreiben, an die ich denken könnte, um das Problem zu demonstrieren. Aber danke, das ist ein guter Tipp. – tschwab

+0

Nur um zu verdeutlichen, wollen Sie die Seite nicht laden, bis der Web-Socket-Code ausgeführt wurde? Du sagst auch, dass es asynchron sein sollte, also bin ich verwirrt. Sie können eine Async-Funktion innerhalb einer Route verwenden, da die Route ohne Informationen aus der Async-Methode zurückkehrt. – Cereal

Antwort

0

Das Problem war, dass async-sinatra seine eigenen Threads verwendet wurde, und so war websocket-client-simple. Die Lösung besteht darin, Bindungen und die eval-Funktion zu verwenden, obwohl dies überhaupt nicht sehr effizient ist. Ich hoffe, dass Optimierungen oder bessere Lösungen zur Verfügung stehen.

require 'sinatra' 
require 'sinatra/async' 
require 'websocket-client-simple' 

set :server, 'thin' 

register Sinatra::Async 

aget '/' do 
    puts 'Request received' 

    socketResponse('wss:// ... URL ...', binding) 
end 

def socketResponse(url, b) 
    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' 
     EM.schedule { b.eval " body 'Closed' " } 
    end 

    ws.on :open do 
     puts 'WS open' 

     message = 'A nice message to process' 
     ws.send message 
     puts 'Sent: ' + message 
    end 
end 
Verwandte Themen