【问题标题】:WebSocket handshake with Ruby and EM::WebSocket::ServerWebSocket 与 Ruby 和 EM::WebSocket::Server 握手
【发布时间】:2013-06-28 22:14:34
【问题描述】:

我正在尝试在 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);
};

鲁比:

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

【问题讨论】:

    标签: javascript ruby websocket em-websocket


    【解决方案1】:
    1. 我认为一旦连接打开,请求和响应就已经发生,所以此时发送标头为时已晚。此外,标题必须以空行结尾,您已将其省略。

    2. 根据演示,您甚至不必在客户端或服务器中设置标头——ruby 模块自动处理服务器端的标头,html5 自动处理客户端的标头边。我认为这应该可行:

      需要“em-websocket-server”

      类 EchoServer

      def on_connect EM::WebSocket::Log.debug "已连接" “我感觉到了一种联系。” 结束

      def on_receive 消息 提出“收到:#{msg}” 发送消息消息 结束

      结束

      EM.run 做 我的主机=“0.0.0.0” 我的端口 = 8000 提出“正在启动 WebSocket 服务器。正在侦听端口 #{myport}...” EM.start_server myhost、myport、EchoServer 结束

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

    我注意到在 Firebug 和 Safari 开发者工具中,服务器都没有发送 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。

    1. 你有什么理由必须使用 em-websocket-server 模块吗?在 github 上对该模块的最后一次修改是三年前。每当您在 ruby​​ 代码中看到 require rubygems 时,都会提醒您代码已过时。我尝试了较新的 em-websocket 模块,并且能够在 Firefox 21.0 和 Safari 5.1.9 上使用 websocket 成功地来回传输数据:

      需要'em-websocket'

      我的主机 = "0.0.0.0" 我的端口 = 8000

      EM.run { 将“监听端口 #{myport}...”

      EM::WebSocket.run(:host => myhost, :port => myport, :debug => false) 做 |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}"
       }
      

      结束 }

    相同的客户端代码。现在响应头包括 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 中搜索 websockets 版本 76 会产生一个过时的协议(其中包含请求和响应的示例):

    https://datatracker.ietf.org/doc/html/draft-hixie-thewebsocketprotocol-76

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

    https://www.rfc-editor.org/rfc/rfc6455

    结论:em-websockets-server 已过时

    【讨论】:

    • 优秀;谢谢你!有用。一个小的更正:EM::WebSocket.run 需要是 EM::WebSocket.start。我编辑了您的答案以反映这一点。
    • @Chad Johnson,请不要编辑我的帖子。我觉得这非常令人反感。当我发布代码时,它是我测试过的代码,也是我所支持的。如果代码不适合您,请撰写您自己的帖子来讨论它为什么不工作和/或发布其他解决方案。通过编辑我的帖子,您歪曲了我所说的内容。我非常感谢您至少通知我您编辑了我的帖子。 em::websocket 的第一个演示使用 run() 方法,我看到其他演示使用 start() 方法。我检查了文档,我不明白 run() 和 start() 之间的区别
    • 如果 run() 对您不起作用,发布您使用的浏览器和操作系统可能会有所帮助。我将 mac osx 10.6.8 与 Safari 5.1.9 和 Firefox 21 一起使用,并且 run() 为我工作。
    • 我通过 run() 得到“EventMachine::WebSocket:Module (NoMethodError) 的未定义方法‘run’”。我在 10.7.5 上使用 ruby​​ 1.9.3p194(2012-04-20 修订版 35410)[x86_64-darwin11.4.2]。 Safari 和 Firefox 是客户端,与 Ruby 无关。
    • 我也在使用来自github.com/igrigorik/em-websocket 的最新代码(0.5)。 Gemfile 行 = gem 'em-websocket', :git => 'git://github.com/igrigorik/em-websocket.git'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-13
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    • 2018-10-16
    相关资源
    最近更新 更多