2010-06-08 4 views
35

Ich habe jetzt für einige Zeit Sinatra benutzen und ich mag einige Echtzeit-Features auf meinen Web-App hinzufügen, indem die Daten über WebSockets schieben.Irgendein Erfolg mit Sinatra, das mit EventMachine WebSockets zusammenarbeitet?

Ich habe erfolgreich den Edelstein 'em-websocket' allein benutzt, aber war nicht in der Lage, eine Ruby-Datei zu schreiben, die einen Sinatra-Webserver UND einen Web-Socket-Server hat.

Ich habe versucht, den Lauf Spinnen! oder anfangen! Methoden in separaten Threads ohne Erfolg deaktiviert.

Hat jemand das zur Arbeit gebracht?

möchte ich sie in der gleichen Datei haben, wie ich dann Variablen zwischen den beiden Servern gemeinsam nutzen können.

Danke!

Antwort

26

Versuchten es nicht, aber sollte nicht allzu schwer sein:

require 'em-websocket' 
require 'sinatra/base' 
require 'thin' 

EM.run do 
    class App < Sinatra::Base 
    # Sinatra code here 
    end 

    EM::WebSocket.start(:host => '0.0.0.0', :port => 3001) do 
    # Websocket code here 
    end 

    # You could also use Rainbows! instead of Thin. 
    # Any EM based Rack handler should do. 
    Thin::Server.start App, '0.0.0.0', 3000 
end 

Auch Cramp hat eine WebSocket-Implementierung, die direkt mit Thin/Rainbows funktioniert! Sie können möglicherweise extrahieren, so dass Sie nicht einmal den Server auf einem anderen Port ausführen müssen.

+0

Das ist so ziemlich wie ich es getan habe. Ich habe eine verwandte Frage, aber wie dekodiere ich den 'Rack :: Server :: Cookie' (gesetzt in der Klasse 'Sinatra'), der in dem' Handshake' zurückgegeben wird, den der Client an 'ws.onopen' übergeben hat. Siehe meine Frage im Detail unter http://stackoverflow.com/questions/16312024/how-to-decode-a-cookie-von-the-header-of-a-websocket-connection-handshake-rub –

20

Dank Konstantin ... das hat funktioniert! Ich musste deinen Code ein wenig optimieren. Ich habe Kommentare hinzugefügt, wo ich es geändert habe.

-poul

require 'rubygems'  # <-- Added this require 
require 'em-websocket' 
require 'sinatra/base' 
require 'thin' 

EventMachine.run do  # <-- Changed EM to EventMachine 
    class App < Sinatra::Base 
     get '/' do 
      return "foo" 
     end 
    end 

    EventMachine::WebSocket.start(:host => '0.0.0.0', :port => 8080) do |ws| # <-- Added |ws| 
     # Websocket code here 
     ws.onopen { 
      ws.send "connected!!!!" 
     } 

     ws.onmessage { |msg| 
      puts "got message #{msg}" 
     } 

     ws.onclose { 
      ws.send "WebSocket closed" 
     } 

    end 

    # You could also use Rainbows! instead of Thin. 
    # Any EM based Rack handler should do. 
    App.run!({:port => 3000}) # <-- Changed this line from Thin.start to App.run! 
end 
+0

Dies funktioniert gut . Eine Frage jedoch: Wie können EventMachine-Socket-Ereignisse auf die Sitzungsinformationen zugreifen, um zu authentifizieren, dass Ereignisse von einem ordnungsgemäß authentifizierten Benutzer stammen? –

+0

Ich habe dies gemäß meinem Kommentar zu @Konstanti Haase's Antwort, als eine neue Frage - siehe http://stackoverflow.com/questions/16312024/how-to-decode-a-cookie-from-the-header- von-a-websocket-connection-handshake-rub –

7

FYI, können Sie auch Padrino-Apps nutzen, mit eventmachine (wie sie Untergruppen von Sinatra-Anwendungen sind):

require 'rubygems' 
require 'eventmachine' 
require 'padrino-core' 
require 'thin' 
require ::File.dirname(__FILE__) + '/config/boot.rb' 

EM.run do 
    Thin::Server.start Padrino.application, '0.0.0.0', 3000 
end 

Cheers, Mike

17

Ich stolperte auf dieses websocket-rack github-Projekt, das im Grunde ein em-websocket rackified und tatsächlich bekam es mit einem Sina schön Seite an Seite zu arbeiten tra app. Hier ist meine config.ru:

require 'rubygems' 
require 'rack/websocket' 
require 'sinatra/base' 

class WebSocketApp < Rack::WebSocket::Application 
    # ... 
end 

class SinatraApp < Sinatra::Base 
    # ... 
end 

map '/ws' do 
    run WebSocketApp.new 
end 

map '/' do 
    run SinatraApp 
end 

Viel Spaß!
Colin

+0

Colin - nur um klar zu sein, das ist in Ihrer config.ru-Datei? – wchrisjohnson

+2

Er sagt buchstäblich "Hier ist meine Konfiguration.ru: ". Also denke ich" Ja "ist die Antwort :) –

11

Ich habe sinatra-websocket verwendet. Lassen Sie den Websocket-Server im selben Prozess und auf demselben Port wie Sinatra laufen.

Disclaimer: Ich bin der Betreuer.

require 'sinatra' 
require 'sinatra-websocket' 

set :server, 'thin' 
set :sockets, [] 

get '/' do 
    if !request.websocket? 
    erb :index 
    else 
    request.websocket do |ws| 
     ws.onopen do 
     ws.send("Hello World!") 
     settings.sockets << ws 
     end 
     ws.onmessage do |msg| 
     EM.next_tick { settings.sockets.each{|s| s.send(msg) } } 
     end 
     ws.onclose do 
     warn("websocket closed") 
     settings.sockets.delete(ws) 
     end 
    end 
    end 
end 

__END__ 
@@ index 
<html> 
    <body> 
    <h1>Simple Echo & Chat Server</h1> 
    <form id="form"> 
     <input type="text" id="input" value="send a message"></input> 
    </form> 
    <div id="msgs"></div> 
    </body> 

    <script type="text/javascript"> 
    window.onload = function(){ 
     (function(){ 
     var show = function(el){ 
      return function(msg){ el.innerHTML = msg + '<br />' + el.innerHTML; } 
     }(document.getElementById('msgs')); 

     var ws  = new WebSocket('ws://' + window.location.host + window.location.pathname); 
     ws.onopen = function() { show('websocket opened'); }; 
     ws.onclose = function() { show('websocket closed'); } 
     ws.onmessage = function(m) { show('websocket message: ' + m.data); }; 

     var sender = function(f){ 
      var input  = document.getElementById('input'); 
      input.onclick = function(){ input.value = "" }; 
      f.onsubmit = function(){ 
      ws.send(input.value); 
      input.value = "send a message"; 
      return false; 
      } 
     }(document.getElementById('form')); 
     })(); 
    } 
    </script> 
</html> 
+0

.. ist dies der schnellste Weg, um Websocket mit Sinatra (in meinen Augen) zu verwenden, kann ich fragen, welche Nachteile dieser Ansatz im Vergleich zur Verwendung von 'EM-Websocket' bringt , Subklassenbildung Sinatra :: Base und "manuell" Einstecken in die Ereignisschleife? –

+0

könnte auch jemand eine kurze Erklärung geben, warum "next_tick" hier benötigt wird? –

+0

@@ simulacre - Irgendwelche Ideen, wie dies auf einer Last arbeiten balanciertes Setup? Ich meine aus dem obigen Beispiel, würde jeder Sinatra-Prozess dann ein eigenes "settings.sockets" -Array haben, das den Prozess stateful macht? – rebnoob