与Ruby和EM :: WebSocket :: Server的WebSocket握手 [英] WebSocket handshake with Ruby and EM::WebSocket::Server

查看:100
本文介绍了与Ruby和EM :: WebSocket :: Server的WebSocket握手的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a simple WebSocket connection in JavaScript against my Rails app. I get the following:


与'ws:// localhost:4000 /'的WebSocket连接失败:WebSocket握手期间出错:'缺少WebSocket-Accept'标题

WebSocket connection to 'ws://localhost:4000/' failed: Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing

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

What am I doing wrong? Here is my code:

JavaScript:

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)我认为一旦连接打开,请求和响应已经发生,所以在那时发送标题为时已晚。此外,标题必须以空白行结束,您省略。

1) I think that once the connection is open the request and response have already occurred, so sending headers at that point is too late. In addition, headers have to end with a blank line, which you omitted.

2)根据演示,您甚至不必在客户端设置标题或服务器 - ruby​​模块自动处理服务器端的标头,html5自动处理客户端的标头。我认为这应该有效:

2) According to the demos, you don't even have to set headers in the client or the server--the ruby module automatically takes care of the headers on the server side, and html5 automatically takes care of the headers on the client side. I think this should work:

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中不起作用:我收到错误消息...

And it does work in Safari 5.1.9 (which is an older browser): I see the expected output on both the server and the client. However, the code does not work in Firefox 21: I get the error message...

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-接受标题:

I notice that in both Firebug and Safari Developer Tools, the server does not send a Sec-WebSocket-Accept header:

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,我去了:

Nothing I tried would make the code work in Firefox 21.0. To check whether Firefox 21.0 even supports websockets, I went to:

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

它说我的浏览器支持websockets。

and it said my browser does support websockets.

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

3) Is there any reason you have to use the em-websocket-server module? The last modification for that module on github was three years ago. And whenever you see require rubygems in ruby code, that should alert you that the code is old. I tried the newer em-websocket module, and I was able to successfully transfer data back and forth using websockets on both Firefox 21.0 and 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
}

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

Same client side code. Now the response headers include 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模块未能在响应中设置该标头时,您的浏览器会拒绝连接。

In your code, I don't think you are setting any headers. Instead, you are just sending messages back and forth that happen to contain characters that look like headers. Apparently, your browser requires the Sec-WebSocket-Accept header in the response before it will allow the connection, and when the em-websocket-server module fails to set that header in the response, your browser refuses the connection.

em-websockets-server的相关源代码如下所示:

The relevant source code for em-websockets-server looks like this:

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会产生一个过时的协议(其中包含请求和响应的示例):

As you can see, it doesn't set the Sec-WebSocket-Accept header. That code is in a module called Version76, and searching google for websockets version 76 yields an obsolete protocol(which contains an example of a request and response):

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

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

Here is the current websockets protocol(which also contains an example of a request and response):

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

结论: em-websockets-server已过时

这篇关于与Ruby和EM :: WebSocket :: Server的WebSocket握手的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆