javascript - 使用 Ruby 和 EM::WebSocket::Server 的 WebSocket 握手

标签 javascript ruby websocket em-websocket

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

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

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

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

握手 header 符合 Wikipedia .

最佳答案

  1. 我认为一旦连接打开,请求和响应就已经发生,所以此时发送 header 为时已晚。此外,标题必须以空白行结尾,而您忽略了这一点。

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

    需要“em-websocket-server”

    类 EchoServer < EM::WebSocket::Server

    def on_connect EM::WebSocket::Log.debug“已连接” 放“我觉得有联系。” 结束

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

    结束

    EM.运行 我的主机=“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 Developer Tools 中,服务器都没有发送 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,我去了:

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

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

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

    需要'em-websocket'

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

    EM.运行{ 放“监听端口#{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}"
     }
    

    结束

相同的客户端代码。现在响应 header 包括 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

在您的代码中,我认为您没有设置任何 header 。相反,您只是来回发送恰好包含看起来像标题的字符的消息。显然,您的浏览器在允许连接之前需要响应中的 Sec-WebSocket-Accept header ,并且当 em-websocket-server 模块无法在响应中设置该 header 时,您的浏览器将拒绝连接。

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 header 。该代码位于一个名为 Version76 的模块中,在 google 上搜索 websockets version 76 会得到一个过时的协议(protocol)(其中包含一个请求和响应的示例):

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

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

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

结论:em-websockets-server 已过时

关于javascript - 使用 Ruby 和 EM::WebSocket::Server 的 WebSocket 握手,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17374453/

相关文章:

javascript - 使用 multer 上传多个文件?

ruby-on-rails - 在您的 Gemfile 中列出的或此机器上可用的任何 gem 源中找不到 gem 'pg (= 0.12.4) ruby'

ruby-on-rails - Mongoid 不在查询中

javascript - 如何使用 node.js、sockets.io 和 redis 在已连接的客户端上触发事件?

javascript - 如何从我的 Node js 应用程序访问谷歌照片?

javascript - 我的 useState 有问题吗?我觉得我的 Array ReactNative 中的值不正确

javascript - 避免 navbar-toggler 在选择时改变颜色

ruby - 检查字符串末尾的子字符串

java - Netty:如何将查询字符串与 websocket 一起使用?

java - 为什么InputStreamReader无法读取websocket包的内容?