ws4py.websocket: Closing connection from client long delay

2.1k Views Asked by At

I created a secure WebSocket server using the ws4py.websocket package.

A javascript client connects to this Websocket and sends some JSON messages to it.

Now when my JS client closes the connection (function disconnect), it takes about 30 secondes before the server's closed function gets called

Here's the code:

Server

class ControlWebSocket(WebSocket):

    def opened(self):
        print("Client connected")

    def closed(self, code, reason=None):
        print("Connection closed [%d]" % (code))

    def received_message(self, m):
        #some business logic using 'm'
server = make_server('', 8999, server_class=WSGIServer, handler_class=WebSocketWSGIRequestHandler, app=WebSocketWSGIApplication(handler_cls=ControlWebSocket))
server.socket = ssl.wrap_socket (server.socket, certfile='./cert.pem', keyfile='key.pem', server_side=True)
server.initialize_websockets_manager()
server.serve_forever()

Client

ws = new WebSocket("wss://192.168.42.1:8999/");

ws.onopen = function() {
    console.log("Connected to WebSocket server");
};

ws.onmessage = function(e) {
    //some business logic using e
};

ws.onclose = function() {
    console.log("Disconnected from WebSocketServer");
};

function disconnect() {
    console.log("Disconnecting from WebSocketServer");
    ws.close();
}

Any clues on why this is so long to close the connection ? Are there any ways to terminate the connection faster ?

1

There are 1 best solutions below

2
On BEST ANSWER

This your client side issue. If you look in the ws module. At the top you have

const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.

And then you have below code

  if (!this._finalized) {
    if (this._closeFrameReceived) this._socket.end();

    //
    // Ensure that the connection is cleaned up even when the closing
    // handshake fails.
    //
    this._closeTimer = setTimeout(this._finalize, closeTimeout, true);
  }

Which is to ensure the connection doesn't close before 30 second and gives each party enough time to cleanup the connection. This time is a const and not configurable as such. But if you want the termination to happen instantly you can add a ws.finalize() after ws.close() and it would disconnect immediately

const WebSocket = require('ws');
let ws = new WebSocket("wss://127.0.0.1:8999/", {
    rejectUnauthorized: false
});

ws.onopen = function() {
    console.log("Connected to WebSocket server");
};

ws.onmessage = function(e) {
    //some business logic using e
};

ws.onclose = function() {
    console.log("Disconnected from WebSocketServer");
};

function disconnect() {
    console.log("Disconnecting from WebSocketServer");
    ws.close();
    ws.finalize();
}

setTimeout(disconnect, 100)

Edit-1

After digging deeper it seems the ws4py has some stuff not in accordance to rfc6455.

When you do a close from NodeJS, it sends a close frame of \x88\x80 which indicates that connection should be closed and a close frame should be sent. The file ws4py/streaming.py has some issues where it doesn't respond to this frame properly. It basically expects more data to be read and gets stuck waiting for the same. This cause the connection to be closed from the client side with the default 30 second timeout.

So this is a bug in the python library which you are using. I created a client using NodeJS WebSocket.server it closed properly.

Why don't you use the Server in NodeJS itself? There is a great example for doing the same at below link

https://github.com/websockets/ws/blob/master/examples/ssl.js

code here for reference

'use strict';

const https = require('https');
const fs = require('fs');

const WebSocket = require('..');

const server = https.createServer({
  cert: fs.readFileSync('../test/fixtures/certificate.pem'),
  key: fs.readFileSync('../test/fixtures/key.pem')
});

const wss = new WebSocket.Server({ server });

wss.on('connection', function connection (ws) {
  ws.on('message', function message (msg) {
    console.log(msg);
  });
});

server.listen(function listening () {
  //
  // If the `rejectUnauthorized` option is not `false`, the server certificate
  // is verified against a list of well-known CAs. An 'error' event is emitted
  // if verification fails.
  //
  // The certificate used in this example is self-signed so `rejectUnauthorized`
  // is set to `false`.
  //
  const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
    rejectUnauthorized: false
  });

  ws.on('open', function open () {
    ws.send('All glory to WebSockets!');
  });
});