2013-06-28 92 views
2

我想在我的Rails應用程序中使用JavaScript創建一個簡單的WebSocket連接。我得到如下:WebSocket與Ruby和EM握手:: WebSocket ::服務器

WebSocket連接到 'WS://本地主機:4000 /' 失敗:錯誤的WebSocket握手期間: '二段的WebSocket-接受' 標頭缺少

我是什麼做錯了?這裏是我的代碼:

的JavaScript:

var socket = new WebSocket('ws://localhost:4000'); 

socket.onopen = function() { 
    var handshake = 
    "GET/HTTP/1.1\n" + 
    "Host: localhost\n" + 
    "Upgrade: websocket\n" + 
    "Connection: Upgrade\n" + 
    "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\n" + 
    "Sec-WebSocket-Protocol: quote\n" + 
    "Sec-WebSocket-Version: 13\n" + 
    "Origin: http://localhost\n"; 

    socket.send(handshake); 
}; 

socket.onmessage = function(data) { 
    console.log(data); 
}; 

紅寶石:

require 'rubygems' 
require 'em-websocket-server' 

module QuoteService 
    class WebSocket < EventMachine::WebSocket::Server 
    def on_connect 
     handshake_response = "HTTP/1.1 101 Switching Protocols\n" 
     handshake_response << "Upgrade: websocket\n" 
     handshake_response << "Connection: Upgrade\n" 
     handshake_response << "Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\n" 
     handshake_response << "Sec-WebSocket-Protocol: quote\n" 

     send_message(handshake_response) 
    end 

    def on_receive(data) 
     puts 'RECEIVED: ' + data 
    end 
    end 
end 

EventMachine.run do 
    print 'Starting WebSocket server...' 
    EventMachine.start_server '0.0.0.0', 4000, QuoteService::WebSocket 
    puts 'running' 
end 

握手頭是每Wikipedia

回答

6

1)我認爲,一旦連接是打開已經發生的請求和響應,在這一點上,以便發送標題是爲時已晚。另外,標題必須以空行結束,您省略。

2)根據演示,你甚至不用在客戶端或服務器設置頭 - 紅寶石模塊自動進行服務器端的護理頭,和HTML5自動處理的頭在客戶端。我想,這應該工作:

require "em-websocket-server" 

class EchoServer < EM::WebSocket::Server 

    def on_connect 
    EM::WebSocket::Log.debug "Connected" 
    puts "I felt a connection." 
    end 

    def on_receive msg 
    puts "RECEIVED: #{msg}" 
    send_message msg 
    end 

end 

EM.run do 
    myhost = "0.0.0.0" 
    myport = 8000 
    puts "Starting WebSocket server. Listening on port #{myport}..." 
    EM.start_server myhost, myport, EchoServer 
end 

HTML文件:

<!DOCTYPE html> <html> <head><title>Test</title> 

<script type="text/javascript"> 

    var myWebSocket = new WebSocket("ws://localhost:8000"); 

    myWebSocket.onopen = function(evt) { 
    console.log("Connection open. Sending message..."); 
    myWebSocket.send("Hello WebSockets!");  }; 

    myWebSocket.onmessage = function(evt) { 
    console.log(evt.data); 
    myWebSocket.close(); }; 

    myWebSocket.onclose = function(evt) { 
    console.log("Connection closed."); }; 

    myWebSocket.onerror = function(err) { 
    alert(err.name + " => " + err.message); } </script> 

</head> <body> <div>Hello</div> </body> </html> 

而且它的工作在Safari 5.1.9(這是一個較舊的瀏覽器):我看到這兩個服務器上的預期輸出和客戶端。但是,代碼無法在Firefox 21的工作:我得到的錯誤信息......

Firefox can't establish a connection to the server at ws://localhost:8000/. 
    var myWebSocket = new WebSocket("ws://localhost:8000"); 

我注意到,在這兩種螢火蟲和Safari的開發工具,服務器不發送二段的WebSocket-Accept報頭:

Response Headers 

Connection   Upgrade 
Upgrade   WebSocket 
WebSocket-Location ws://localhost:8000/ 
WebSocket-Origin null 


Request Headers 

Accept     text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Encoding   gzip, deflate 
Accept-Language   en-US,en;q=0.5 
Cache-Control   no-cache 
Connection   keep-alive, Upgrade 
DNT     1 
Host     localhost:8000 
Origin     null 
Pragma     no-cache 
Sec-WebSocket-Key r9xT+ywe533EHF09wxelkg== 
Sec-WebSocket-Version 13 
Upgrade     websocket 
User-Agent   Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0 

沒有什麼我試圖會使代碼工作在Firefox 21.0。要檢查Firefox 21.0是否支持websocket,我去了:

http://www.websocket.org/echo.html 

它說我的瀏覽器支持websockets。

3)是否有任何理由必須使用em-websocket-server模塊? github上該模塊的最後修改是三年前的。只要你在ruby代碼中看到require rubygems,那應該提醒你代碼已經老了。我嘗試了新的EM-的WebSocket模塊,我是能夠成功地傳遞數據來回使用的WebSockets兩個火狐21.0和Safari 5.1.9:

require 'em-websocket' 

myhost = "0.0.0.0" 
myport = 8000 

EM.run { 
    puts "Listening on port #{myport}..." 

    EM::WebSocket.run(:host => myhost, :port => myport, :debug => false) do |ws| 

    ws.onopen do |handshake| 
     path = handshake.path 
     query_str = handshake.query 
     origin = handshake.origin 

     puts "WebSocket opened:" 
     puts "\t path \t\t -> #{path}" 
     puts "\t query_str \t -> #{query_str}" 
     puts "\t origin \t -> #{origin}" 
    end 

    ws.onmessage { |msg| 
     ws.send "Pong: #{msg}" 
    } 
    ws.onclose { 
     puts "WebSocket closed" 
    } 
    ws.onerror { |e| 
     puts "Error: #{e.message}" 
    } 
    end 
} 

同一個客戶端代碼。現在,響應頭包括二段的WebSocket-接受:

Response Headers 

Connection   Upgrade 
Sec-WebSocket-Accept LyIm6d+kAAqkcTR744tVK9HMepY= 
Upgrade     websocket 


Request Headers 

Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Encoding gzip, deflate 
Accept-Language en-US,en;q=0.5 
Cache-Control no-cache 
Connection keep-alive, Upgrade 
DNT 1 
Host localhost:8000 
Origin null 
Pragma no-cache 
Sec-WebSocket-Key pbK8lFHQAF+arl9tFvHn/Q== 
Sec-WebSocket-Version 13 
Upgrade websocket 
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0 

在你的代碼,我不認爲你設置任何頭。相反,您只是來回發送包含看起來像標題的字符的消息。很明顯,瀏覽器在允許連接之前需要在響應中使用Sec-WebSocket-Accept標頭,並且當em-websocket-server模塊在響應中未能設置該標頭時,瀏覽器拒絕連接。

爲EM-的WebSockets服務器相關的源代碼如下所示:

module EM 
    module WebSocket 
    module Protocol 
     module Version76 

     # generate protocol 76 compatible response headers 
     def response 
      response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" 
      response << "Upgrade: WebSocket\r\n" 
      response << "Connection: Upgrade\r\n" 
      response << "Sec-WebSocket-Origin: #{origin}\r\n" 
      response << "Sec-WebSocket-Location: #{scheme}://#{host}#{path}\r\n" 

      if protocol 
      response << "Sec-WebSocket-Protocol: #{protocol}\r\n" 
      end 

      response << "\r\n" 
      response << Digest::MD5.digest(keyset) 

      response 
     end 

正如你所看到的,它不設置二段的WebSocket-Accept報頭。該代碼是一種稱爲Version76模塊中,以及用於的WebSockets版本76谷歌搜索產生一個過時的協議(其中包含的請求和響應的例子):

http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76

這裏是當前的WebSockets協議(它也包含請求和響應)的一個例子:

http://tools.ietf.org/html/rfc6455

結論:EM-的WebSockets服務器是過時

+0

優秀;謝謝!有用。一個小小的更正:EM :: WebSocket.run需要EM :: WebSocket.start。我編輯了你的答案來反映這一點。 –

+0

@ Chad Johnson,請不要編輯我的帖子。我覺得這非常冒犯。當我發佈代碼時,它是我測試過的代碼,這是我所支持的。如果代碼不適用於您,請編寫自己的帖子,討論爲什麼它不起作用和/或發佈其他解決方案。通過編輯我的文章,你錯誤地陳述了我所說的話。我很感謝你至少通知我你編輯了我的帖子。 em :: websocket的第一個演示使用run()方法,我發現其他演示使用start()方法。我檢查了文檔,並且我不明白run()和start() – 7stud

+0

之間的區別。如果run()不適合您,也許發佈您使用的瀏覽器和操作系統會很有幫助。我在Safari 5.1.9和Firefox 21上使用了mac osx 10.6.8,並且run()爲我工作。 – 7stud