Threaded, non-blocking websocket client

Question:

I am wanting to run a program in Python that sends a message every second via web sockets to a Tornado server. I have been using the example on websocket-client;

This example does not work, because ws.run_forever() will stop the execution of the while loop.

Can somebody give me an example of how to correctly implement this as a threaded class which I can both call the send method of, but also receive messages?

import websocket
import thread
import time

def on_message(ws, message):
    print message

def on_error(ws, error):
    print error

def on_close(ws):
    print "### closed ###"

def on_open(ws):
    pass

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://echo.websocket.org/", on_message = on_message, on_error = on_error, on_close = on_close)
    ws.on_open = on_open
    ws.run_forever()

    while True:
        #do other actions here... collect data etc.
        for i in range(100):
            time.sleep(1)
            ws.send("Hello %d" % i)
        time.sleep(1)
Asked By: Chris

||

Answers:

There’s an example in their github page that does exactly that. It seems like you started out of that example and took the code that sends messages every second out of the on_open and pasted it after the run_forever call, that BTW runs until the socket is disconnected.

Maybe you are having issues with the basic concepts here. There’s always going to be a thread dedicated to listening to the socket (in this case the main thread that enters a loop inside the run_forever waiting for messages). If you want to have some other thing going on you’ll need another thread.

Below is a different version of their example code, where instead of using the main thread as the “socket listener”, another thread is created and the run_forever runs there. I see it as a bit more complicated since you have to write code to assure the socket has connected while you could use the on_open callback, but maybe it will help you understand.

import websocket
import threading
from time import sleep

def on_message(ws, message):
    print message

def on_close(ws):
    print "### closed ###"

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://echo.websocket.org/", on_message = on_message, on_close = on_close)
    wst = threading.Thread(target=ws.run_forever)
    wst.daemon = True
    wst.start()

    conn_timeout = 5
    while not ws.sock.connected and conn_timeout:
        sleep(1)
        conn_timeout -= 1

    msg_counter = 0
    while ws.sock.connected:
        ws.send('Hello world %d'%msg_counter)
        sleep(1)
        msg_counter += 1
Answered By: Ricardo Gemignani

In 2023, they have an updated example for dispatching multiple WebSocketApps using an asynchronous dispatcher like rel.

import websocket, rel

addr = "wss://api.gemini.com/v1/marketdata/%s"
for symbol in ["BTCUSD", "ETHUSD", "ETHBTC"]:
    ws = websocket.WebSocketApp(addr % (symbol,), on_message=lambda w, m : print(m))
    ws.run_forever(dispatcher=rel, reconnect=3)  
rel.signal(2, rel.abort)  # Keyboard Interrupt  
rel.dispatch()

Hope it helps!

Answered By: hmartos
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.