AnilJ AnilJ - 23 days ago 14
Python Question

Multithreaded websocket client does not exit properly

I wrote a multithreaded web-socket client class so that the user's (main) thread does not blocks on the 'run_forever()' method call. The code seems to work fine, except in the end, when I am stopping the thread, it does not close the web-socket cleanly and my process does not exit. I have to do a 'kill -9' each time to get rid of it. I tried calling the thread's join() method to make sure the main thread wait for the the child to complete its execution, but that did not help.

The code looks like below. Can you please help me make the exit/stopping of the thread graceful?

#!/usr/bin/python

import thread
import threading
import time
import websocket

class WebSocketClient(threading.Thread):

def __init__(self, url):
self.url = url
threading.Thread.__init__(self)

def run(self):

# Running the run_forever() in a seperate thread.
#websocket.enableTrace(True)
self.ws = websocket.WebSocketApp(self.url,
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close)
self.ws.on_open = self.on_open
self.ws.run_forever()

def send(self, data):

# Wait till websocket is connected.
while not self.ws.sock.connected:
time.sleep(0.25)

print 'Sending data...', data
self.ws.send("Hello %s" % data)

def stop(self):
print 'Stopping the websocket...'
self.ws.keep_running = False

def on_message(self, ws, message):
print 'Received data...', message

def on_error(self, ws, error):
print 'Received error...'
print error

def on_close(self, ws):
print 'Closed the connection...'

def on_open(self, ws):
print 'Opened the connection...'

if __name__ == "__main__":

wsCli = WebSocketClient("ws://localhost:8888/ws")
wsCli.start()
wsCli.send('Hello')
time.sleep(.1)
wsCli.send('World')
time.sleep(1)
wsCli.stop()
#wsCli.join()
print 'After closing client...'

Answer

To summarize the solution discovered through our extended discussion in the comments (and give you a chance to accept and up-vote my answer. ;-)

To fix the problem you need to call self.ws.close() in the WebSocketClient class's stop() method instead of merely setting self.ws.keep_running = False. That way the the web-socket will be cleanly closed.

I suggested you also set the thread's daemon attribute to True so the thread will be automatically be stopped when the main thread terminates for any reason—just in case something totally unexpected happens.