[英]WebSocket handshake with Ruby and EM::WebSocket::Server
我正在尝试针对我的Rails应用程序在JavaScript中创建一个简单的WebSocket连接。 我得到以下内容:
WebSocket与'ws:// localhost:4000 /'的连接失败: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的 。
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文件:
<!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是否甚至支持websocket,我去了:
http://www.websocket.org/echo.html
它说我的浏览器确实支持websockets。
3)有什么理由需要使用em-websocket-server模块? 在github上对该模块的最后修改是三年前。 而且,每当您看到require rubygems
在ruby代码中使用require rubygems
时,都应该提醒您该代码已过时。 我尝试了较新的em-websocket模块,并且能够在Firefox 21.0和Safari 5.1.9上使用websocket成功地来回传输数据:
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中搜索websockets版本76会产生一个过时的协议(其中包含请求和响应的示例):
http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
这是当前的websockets协议(还包含一个请求和响应的示例):
http://tools.ietf.org/html/rfc6455
结论: em-websockets-server已过时 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.