我正在尝试与龙卷风服务器建立WS连接.服务器代码很简单:
class WebSocketHandler(tornado.websocket.WebSocketHandler): def open(self): print("WebSocket opened") def on_message(self, message): self.write_message(u"You said: " + message) def on_close(self): print("WebSocket closed") def main(): settings = { "static_path": os.path.join(os.path.dirname(__file__), "static") } app = tornado.web.Application([ (r'/ws', WebSocketHandler), (r"/()$", tornado.web.StaticFileHandler, {'path':'static/index.html'}), ], **settings) app.listen(8888) tornado.ioloop.IOLoop.current().start()
我从这里复制粘贴客户端代码:
$(document).ready(function () { if ("WebSocket" in window) { console.log('WebSocket is supported by your browser.'); var serviceUrl = 'ws://localhost:8888/ws'; var protocol = 'Chat-1.0'; var socket = new WebSocket(serviceUrl, protocol); socket.onopen = function () { console.log('Connection Established!'); }; socket.onclose = function () { console.log('Connection Closed!'); }; socket.onerror = function (error) { console.log('Error Occured: ' + error); }; socket.onmessage = function (e) { if (typeof e.data === "string") { console.log('String message received: ' + e.data); } else if (e.data instanceof ArrayBuffer) { console.log('ArrayBuffer received: ' + e.data); } else if (e.data instanceof Blob) { console.log('Blob received: ' + e.data); } }; socket.send("Hello WebSocket!"); socket.close(); } });
当它尝试连接时,我在浏览器的控制台上获得以下输出:
WebSocket connection to 'ws://localhost:8888/ws' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
这是为什么?
正如whatwg.org的Websocket文档中指出的那样(它是标准草案的副本):
WebSocket(url,protocols)构造函数接受一个或两个参数.第一个参数url指定要连接的URL.第二种协议(如果存在)是字符串或字符串数组.如果它是一个字符串,它相当于一个只包含该字符串的数组; 如果省略,则相当于空数组.数组中的每个字符串都是子协议名称.仅当服务器报告已选择其中一个子协议时才建立连接.子协议名称必须都是符合WebSocket协议规范定义的包含Sec-WebSocket-Protocol字段值的元素要求的字符串.
您的服务器使用空Sec-WebSocket-Protocol
标头应答websocket连接请求,因为它不支持子Chat-1
协议.
由于您正在编写服务器端和客户端(除非您编写要分享的API),因此设置特定的子协议名称并不是非常重要.
您可以通过从javascript连接中删除子协议名称来解决此问题:
var socket = new WebSocket(serviceUrl);
或者通过修改服务器以支持所请求的协议.
我可以给出一个Ruby示例,但由于我没有足够的信息,所以我不能给出Python示例.
自从我在评论中被问到,这是一个Ruby示例.
此示例需要iodine
HTTP/WebSockets服务器,因为它支持rack.upgrade
规范草案(此处详述的概念)并添加了pub/sub API.
服务器代码可以通过终端执行,也可以作为config.ru
文件中的Rack应用程序执行(从命令行运行iodine
以启动服务器):
# frozen_string_literal: true class ChatClient def on_open client @nickname = client.env['PATH_INFO'].to_s.split('/')[1] || "Guest" client.subscribe :chat client.publish :chat , "#{@nickname} joined the chat." if client.env['my_websocket.protocol'] client.write "You're using the #{client.env['my_websocket.protocol']} protocol" else client.write "You're not using a protocol, but we let it slide" end end def on_close client client.publish :chat , "#{@nickname} left the chat." end def on_message client, message client.publish :chat , "#{@nickname}: #{message}" end end module APP # the Rack application def self.call env return [200, {}, ["Hello World"]] unless env["rack.upgrade?"] env["rack.upgrade"] = ChatClient.new protocol = select_protocol(env) if protocol # we will use the same client for all protocols, because it's a toy example env['my_websocket.protocol'] = protocol # <= used by the client [101, { "Sec-Websocket-Protocol" => protocol }, []] else # we can either refuse the connection, or allow it without a match # here, it is allowed [101, {}, []] end end # the allowed protocols PROTOCOLS = %w{ chat-1.0 soap raw } def select_protocol(env) request_protocols = env["HTTP_SEC_WEBSOCKET_PROTOCOL"] unless request_protocols.nil? request_protocols = request_protocols.split(/,\s?/) if request_protocols.is_a?(String) request_protocols.detect { |request_protocol| PROTOCOLS.include? request_protocol } end # either `nil` or the result of `request_protocols.detect` are returned end # make functions available as a singleton module extend self end # config.ru if __FILE__.end_with? ".ru" run APP else # terminal? require 'iodine' Iodine.threads = 1 Iodine.listen2http app: APP, log: true Iodine.start end
要测试代码,以下JavaScript应该可以工作:
ws = new WebSocket("ws://localhost:3000/Mitchel", "chat-1.0"); ws.onmessage = function(e) { console.log(e.data); }; ws.onclose = function(e) { console.log("Closed"); }; ws.onopen = function(e) { e.target.send("Yo!"); };