0

I have a question very similar to this one: Send data every 10 seconds via socket.io

But: My server is written in Python, the client is in JavaScript

The goal:

  • Clients connect to server via socket.io
  • Clients receive push messages ping from server every n seconds
  • When a client sends a ping message, the server broadcasts a pong message

What works:

  • Socket.io connection works fine
  • Client ping is received by server, answered with pong, which is again received by client
  • Server executes ping_in_intervals every 5 seconds

What doesn't work:

  • When server executes ping_in_intervals (which triggers sending a ping), that ping is not received by any client
  • When ping_in_intervals loop is active, socket connections crash every minute or so. If the method is commented out, then socket connection stays stable.

Observations:

  • The thread, that ping_in_intervals is running in doesn't seem to properly work together with the wsgi server thread.
  • The ping_in_intervals thread destabilizes the server thred, causes it to loose connections (which are reestablished right away, but they do drop every minute or so)
  • I think, that I'm doing something terribly wrong with threading. I have very little experience with threading in Python and don't know, where to look for the problem

Server:

import eventlet
import socketio
import threading

sio = socketio.Server(cors_allowed_origins="*", async_mode='eventlet')
app = socketio.WSGIApp(sio)


def ping_in_intervals():
    threading.Timer(5.0, ping_in_intervals).start()
    print("send ping")
    sio.emit('ping')


@sio.on('ping')
def ping(*args):
    print("received ping - send pong")
    sio.emit('pong')


ping_in_intervals()
eventlet.wsgi.server(eventlet.listen(('', 8080)), app)

Client:

const socket = io.connect('localhost:8080', {secure: true, transports: ['websocket']});

socket.on('pong', () => {
    console.log('received pong');
});

socket.on('ping', () => {
    console.log('received ping');
});

socket.on('connect', () => {
    socket.emit('ping')
});

  • Does this answer your question? [What is the best way to repeatedly execute a function every x seconds?](https://stackoverflow.com/questions/474528/what-is-the-best-way-to-repeatedly-execute-a-function-every-x-seconds) – 0stone0 Mar 03 '22 at 13:37
  • No. Running `ping_in_intervals` every 5 seconds does work. That's not the problem. What doesn't work: the `ping` emitted by this function doesn't reach the clients and running the function somehow destabilizes the socket connection – Florian Metzger-Noel Mar 03 '22 at 13:44
  • Correct me if I'm wrong, but it looks like `ping_in_intervals` starts by scheduling a repeat timer for itself. Which means the next call schedules _another repeat timer_ for itself. Which then schedules... you get the point. Even if the pings "work", presumably you only want a single timer. – Mike 'Pomax' Kamermans Mar 03 '22 at 18:27
  • Also, which package are you using for that `socketio` import? (there are two, one is as sketchy as sketchy gets, the other is funded by socket.io themselves) – Mike 'Pomax' Kamermans Mar 03 '22 at 18:30
  • `ping_in_intervals` doesn't create more and more loops as `threading.Timer` executes the given function only once after the time is up. Anyways this nothing to do with the problem, though, that the function doesn't emit pings over the socket. I'm using python-socketio 5.5.2 - i checked the offical documentation, it seems to be the officially supported version – Florian Metzger-Noel Mar 15 '22 at 16:05

1 Answers1

0

Found the solution at https://github.com/miguelgrinberg/python-socketio/blob/main/examples/server/wsgi/app.py#L16-L22

The thread, which pushes server messages every n seconds, shouldn't be started using threading, but instead using the start_background_task function of socketio.

Here's the working code:

import eventlet
import socketio

sio = socketio.Server(cors_allowed_origins="*", async_mode='eventlet')
app = socketio.WSGIApp(sio)    

def ping_in_intervals():
    while True:
        sio.sleep(10)
        sio.emit('ping')        

@sio.on('ping')
def ping(*args):
    sio.emit('pong')

thread = sio.start_background_task(ping_in_intervals)
eventlet.wsgi.server(eventlet.listen(('', 8080)), app)