I'm trying to create a WS connection with my tornado server. The server code is simple:
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()
I copy pasted the client code from here:
$(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();
}
});
When it tries to connect i get the following output on the browser's console:
WebSocket connection to 'ws://localhost:8888/ws' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
Why is that?
Solution 1Check that all the Bot Insight services are running. Check that your firewall settings are configured to accept incoming websocket data. Try to use a different web browser. Restart the Bot Insight Visualization and Bot Insight Scheduler services.
- In Control Panel, click Programs and Features, and then click Turn Windows features on or off. Expand Internet Information Services, expand World Wide Web Services, expand Application Development Features, and then select WebSocket Protocol. Click OK. Click Close.
You might be using WebSockets incorrectly if: The connection is used only for a very small number of events, or a very small amount of time, and the client does not need to quickly react to the events. Your feature requires multiple WebSockets to be open to the same service at once.
The WebSocket protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request. By default the WebSocket protocol uses port 80 for regular WebSocket connections and port 443 for WebSocket connections tunneled over TLS [RFC2818].
As pointed out in whatwg.org's Websocket documentation (it's a copy from the standard's draft):
The WebSocket(url, protocols) constructor takes one or two arguments. The first argument, url, specifies the URL to which to connect. The second, protocols, if present, is either a string or an array of strings. If it is a string, it is equivalent to an array consisting of just that string; if it is omitted, it is equivalent to the empty array. Each string in the array is a subprotocol name. The connection will only be established if the server reports that it has selected one of these subprotocols. The subprotocol names must all be strings that match the requirements for elements that comprise the value of Sec-WebSocket-Protocol fields as defined by the WebSocket protocol specification.
Your server answers the websocket connection request with an empty Sec-WebSocket-Protocol
header, since it doesn't support the Chat-1
subprotocol.
Since you're writing both the server side and the client side (and unless your writing an API you intend to share), it shouldn't be super important to set a specific subprotocol name.
You can fix this by either removing the subprotocol name from the javascript connection:
var socket = new WebSocket(serviceUrl);
Or by modifying your server to support the protocol requested.
I could give a Ruby example, but I can't give a Python example since I don't have enough information.
Since I was asked in the comments, here's a Ruby example.
This example requires the iodine
HTTP/WebSockets server, since it supports the rack.upgrade
specification draft (concept detailed here) and adds a pub/sub API.
The server code can be either executed through the terminal or as a Rack application in a config.ru
file (run iodine
from the command line to start the server):
# 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
To test the code, the following JavaScript should work:
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!"); };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With