与Ruby和EM :: WebSocket :: Server的WebSocket握手

我正在尝试使用JavaScript在我的Rails应用程序中创建一个简单的WebSocket连接。 我得到以下内容:

与’ws:// localhost:4000 /’的WebSocket连接失败:WebSocket握手期间出错:’Sec-WebSocket-Accept’标头丢失

我究竟做错了什么? 这是我的代码:

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); }; 

ruby:

 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 

握手标题是每个维基百科 。

1)我认为一旦连接打开,请求和响应已经发生,所以在那时发送标题为时已晚。 此外,标题必须以空行结束,您省略了该行。

2)根据演示,您甚至不必在客户端或服务器中设置标头 – ruby​​模块自动处理服务器端的标头,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文件:

   Test    
Hello

它确实在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"); 

我注意到在Firebug和Safari Developer Tools中,服务器都没有发送Sec-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是否支持websockets,我去了:

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

它说我的浏览器确实支持websockets。

3)您是否有必要使用em-websocket-server模块? github上该模块的最后一次修改是在三年前。 每当你在ruby代码中看到require rubygems时,它应该提醒你代码是旧的。 我尝试了更新的em-websocket模块,并且我能够使用Firefox 21.0和Safari 5.1.9上的websockets成功地来回传输数据:

 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 } 

相同的客户端代码。 现在响应头包括Sec-WebSocket-Accept:

 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-server的相关源代码如下所示:

 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 

如您所见,它没有设置Sec-WebSocket-Accept标头。 该代码位于名为Version76的模块中,并且搜索google for websockets版本76会产生一个过时的协议(其中包含请求和响应的示例):

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

这是当前的websockets协议(其中还包含请求和响应的示例):

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

结论: em-websockets-server已经过时了