0

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
eshapiro42
  • 186
  • 1
  • 2
  • 14
  • 1
    Have you debugged it to determine where exactly things get stuck? – Miguel Grinberg Dec 03 '20 at 18:47
  • The Python side gets stuck in the `name = socketio.call('get name', to=sid)` line. The JavaScript side seems to get so confused that the debugger becomes useless (at least in Chrome). – eshapiro42 Dec 03 '20 at 18:48
  • So you don't even know if the JS side gets the event? How come? – Miguel Grinberg Dec 03 '20 at 18:52
  • Scratch that, sorry, that was for an older version of my code. For the version above, the JavaScript side actually does exactly what is expected, and after returning, a GET request appears in the server logs. But Python still gets stuck in that same line and eventually the `Server.call()` times out. – eshapiro42 Dec 03 '20 at 18:54
  • 1
    Follow the documentation instructions to enable logging in the server: https://python-socketio.readthedocs.io/en/latest/server.html#debugging-and-troubleshooting. – Miguel Grinberg Dec 03 '20 at 19:02
  • I have edited my question with the log output since it was too long to type in a comment. – eshapiro42 Dec 03 '20 at 19:08
  • 1
    This could be a bug related to your exchange happening at the same time the websocket upgrade is being processed. Try connecting via websocket directly like you do in your Python client to see if that makes a difference. – Miguel Grinberg Dec 03 '20 at 23:22
  • I'm not sure how to do that in JavaScript. The official Socket.IO documentation doesn't seem that great and seems mainly focused toward those using Node. Would you mind being a little more specific? – eshapiro42 Dec 03 '20 at 23:29
  • Okay, I figured out how to make the WebSocket upgrade happen immediately (I just had to change the first line of `client.js` to `var socket = io.connect('http://localhost:5000', {transports: ['websocket']});`. It did not solve the issue though. I have updated the log output in my question with the new version. – eshapiro42 Dec 04 '20 at 00:16

1 Answers1

1

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');
});
Miguel Grinberg
  • 65,299
  • 14
  • 133
  • 152
  • Thank you so much! It's so simple and I feel kind of dumb now. But at least I barely have to change my existing code at all. Thanks for all your help :D – eshapiro42 Dec 04 '20 at 14:55