Python-socketio Server.call() method does not seem to work with JavaScript client

1.6k Views Asked by At

Summarize the problem

I am using the python-socketio package as a server for a web-based game that I am creating. I initially implemented the client in Python (with python-socketio[client]) for debugging purposes, and made extensive use of the Server.call() method while developing. This works perfectly with everything running in Python, and will wait for user input before sending the return value of the function on the client side back to the server.

However, when trying to switch things over to JavaScript, Server.call() times out no matter what I do. I am not really aware of any alternatives that I can switch to without having to completely rip apart my already implemented program, but I am open to suggestions.

Describe what you've tried

I've tried reimplementing my Python client code in JavaScript more or less exactly as was originally written. This was a bust, since as I've mentioned above any time I use Server.call() it times out (or hangs indefinitely if I set timeout=None).

I have additionally tried to switch away from Server.call() and instead use Server.emit() with a callback to set a global variable and then block until it is set using Server.sleep(), but this doesn't seem to work either.

Show some code

Here's a minimal example that demonstrates the issue. The server is the same in both use cases and the Python client and JavaScript client are practically identical, but things only work when using the Python client.

Here is the server, written in Python and using the python-socketio module:

# server.py


from socketio import Server, WSGIApp

socketio = Server(async_mode='eventlet', async_handlers=True, cors_allowed_origins='*')

@socketio.on('start')
def start(sid):
    name = socketio.call('get name', to=sid)
    print(name)

if __name__ == '__main__':
    app = WSGIApp(socketio)
    import eventlet
    eventlet.wsgi.server(eventlet.listen(('0.0.0.0', 5000)), app)

Here is the working Python client:

# client.py


import socketio

sio = socketio.Client()

@sio.event
def connect():
    sio.emit('start')

@sio.on('get name')
def get_name():
    print('Sending name')
    return 'Hermione Granger'

if __name__ == '__main__':
    sio.connect('http://localhost:5000', transports=['websocket'])

Here is the non-working JavaScript client and some corresponding HTML, just so anyone reading this has everything they need to try to reproduce my issue:

// client.js


var socket = io.connect('http://localhost:5000');

socket.on('connect', function () {
    socket.emit('start');
});

socket.on('get name', function () {
    console.log('Sending name');
    return 'Hermione Granger';
});
<!-- index.html -->


<!doctype html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
    <script src="client.js"></script>
</body>
</html>

If anyone can help me figure out why this isn't working (or recommend a simple alternative) I will be greatly in your debt! :)

Log outputs from the server

Here are the server logs when I try to run the JavaScript client:

Server initialized for eventlet.
(16261) wsgi starting up on http://0.0.0.0:5000
(16261) accepted ('127.0.0.1', 63733)
2bff7607e60548e5ba70b2dcabb6131d: Sending packet OPEN data {'sid': '2bff7607e60548e5ba70b2dcabb6131d', 'upgrades': [], 'pingTimeout': 60000, 'pingInterval': 25000}
2bff7607e60548e5ba70b2dcabb6131d: Sending packet MESSAGE data 0
2bff7607e60548e5ba70b2dcabb6131d: Received request to upgrade to websocket
2bff7607e60548e5ba70b2dcabb6131d: Upgrade to websocket successful
2bff7607e60548e5ba70b2dcabb6131d: Received packet MESSAGE data 2["start"]
received event "start" from 2bff7607e60548e5ba70b2dcabb6131d [/]
emitting event "get name" to 2bff7607e60548e5ba70b2dcabb6131d [/]
2bff7607e60548e5ba70b2dcabb6131d: Sending packet MESSAGE data 21["get name"]
2bff7607e60548e5ba70b2dcabb6131d: Received packet PING data None
2bff7607e60548e5ba70b2dcabb6131d: Sending packet PONG data None
2bff7607e60548e5ba70b2dcabb6131d: Received packet PING data None
2bff7607e60548e5ba70b2dcabb6131d: Sending packet PONG data None
Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/mnt/c/Users/eshap/Documents/GitHub/dominion-game/venv/lib/python3.8/site-packages/socketio/server.py", line 685, in _handle_event_internal
    r = server._trigger_event(data[0], namespace, sid, *data[1:])
  File "/mnt/c/Users/eshap/Documents/GitHub/dominion-game/venv/lib/python3.8/site-packages/socketio/server.py", line 714, in _trigger_event
    return self.handlers[namespace][event](*args)
  File "server.py", line 7, in start
    name = socketio.call('get name', to=sid)
  File "/mnt/c/Users/eshap/Documents/GitHub/dominion-game/venv/lib/python3.8/site-packages/socketio/server.py", line 386, in call
    raise exceptions.TimeoutError()
socketio.exceptions.TimeoutError

For comparison, here are the server logs when I successfully run the Python client:

Server initialized for eventlet.
(14184) wsgi starting up on http://0.0.0.0:5000
(14184) accepted ('127.0.0.1', 54749)
a5f56db4733b4b1b924b0cb2a599e7c6: Sending packet OPEN data {'sid': 'a5f56db4733b4b1b924b0cb2a599e7c6', 'upgrades': [], 'pingTimeout': 60000, 'pingInterval': 25000}
a5f56db4733b4b1b924b0cb2a599e7c6: Sending packet MESSAGE data 0
a5f56db4733b4b1b924b0cb2a599e7c6: Received request to upgrade to websocket
a5f56db4733b4b1b924b0cb2a599e7c6: Upgrade to websocket successful
a5f56db4733b4b1b924b0cb2a599e7c6: Received packet PING data None
a5f56db4733b4b1b924b0cb2a599e7c6: Sending packet PONG data None
a5f56db4733b4b1b924b0cb2a599e7c6: Received packet MESSAGE data 2["start"]
received event "start" from a5f56db4733b4b1b924b0cb2a599e7c6 [/]
emitting event "get name" to a5f56db4733b4b1b924b0cb2a599e7c6 [/]
a5f56db4733b4b1b924b0cb2a599e7c6: Sending packet MESSAGE data 21["get name"]
a5f56db4733b4b1b924b0cb2a599e7c6: Received packet MESSAGE data 31["Hermione Granger"]
received ack from a5f56db4733b4b1b924b0cb2a599e7c6 [/]
Hermione Granger
1

There are 1 best solutions below

1
On BEST ANSWER

The problem is that you are using the Python style for returning values in your JavaScript version. In JavaScript events are asynchronous, when you want to provide a return value you have to do so with a callback function instead.

Here is the correct way to code the get name event for JavaScript:

socket.on('get name', function (cb) {
    console.log('Sending name');
    cb('Hermione Granger');
});