Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Last active October 3, 2019 10:56
Show Gist options
  • Save mattdesl/484db14fa55835dd1091cd374826db57 to your computer and use it in GitHub Desktop.
Save mattdesl/484db14fa55835dd1091cd374826db57 to your computer and use it in GitHub Desktop.

python websockets + browser

Two approaches:

The sync approach is faster and less complex. ¯_(ツ)_/¯

Please comment if you have ideas about how to make the asyncio cleaner/faster.

1.0 copy + install

First download the gist or copy the files to a folder.

Then install the two dependencies, I'm using pip3 and python3 here.

pip3 install websockets
pip3 install git+https://github.com/Pithikos/python-websocket-server

2.0 run websocket server

Run either asyncio or sync server:

# asyncio version
python3 ws-async.py

# or, sync version
python3 ws-sync.py

3.0 run client server

Once the websocket server is running, now let's run a simple HTTP server:

python3 -m http.server

Now open http://localhost:8000/ and check the console, you should get a stream of data from the websocket server.

License

MIT license.

<script>
const PORT = 32045;
const HOST = '127.0.0.1';
const uri = `ws://${HOST}:${PORT}`;
console.log('Connecting to', uri);
createWebSocket(uri, (data) => {
console.log('got data from websocket server', data);
});
function createWebSocket (uri, cb) {
const ws = new window.WebSocket(uri);
ws.onmessage = function (event) {
try {
const data = JSON.parse(event.data);
cb(data);
} catch (err) {
console.warn('Error: Non-json data in message: %s', event.data);
}
};
ws.onclose = function (ev) {
console.log('websocket closed');
};
ws.onopen = function () {
console.log('websocket open');
};
return ws;
}
</script>
#!/usr/bin/env python
import time
from datetime import datetime
import asyncio
import signal
import websockets
import json
host = '127.0.0.1'
port = 32045
fps = 30
uri = "ws://%s:%s" % (host, port)
loop = asyncio.get_event_loop()
is_running = True
server = None
def get_data ():
# Here you could poll from hardware devices
data = {
'time': datetime.now()
}
return data
async def connection(websocket, path):
print('Socket connected')
try:
await websocket.wait_closed()
except Exception as err:
print('Error waiting for close event in socket')
print(err)
finally:
print('Socket disconnected')
async def send_message(user, message):
try:
await user.send(message)
except websockets.exceptions.ConnectionClosed as err:
if err.code != 1000 and err.code != 1001:
print('Unexpected error code while sending events: %s' % err.code)
print(err)
except Exception as err:
print('Received unexpected socket send error')
print(err)
async def broadcast(message, users):
# Send new data to users
if users:
tasks = [ send_message(user, message) for user in users ]
await asyncio.gather(*tasks, loop=loop, return_exceptions=True)
async def create_server():
global is_running, server
server = await websockets.serve(connection, host, port)
is_running = True
print("""
Running Websocket Server on %s
""" % (uri))
while is_running:
if server.websockets:
# Poll new data from IO
# Need to run in executor as this may involve
# sync time.sleep() from software libs
data = await loop.run_in_executor(None, get_data)
data_str = json.dumps(data, default=str)
await broadcast(data_str, server.websockets)
await asyncio.sleep(1.0 / fps)
# Close the server once we're done
server.close()
await server.wait_closed()
return server
# Flag the server as needing to shutdown its broadcast loop
async def stop_server():
global is_running
# Small safeguard here in case the server is
# stuck closing for some reason, we can still bail
# out easily
if not is_running:
raise SystemExit
print('\nClosing server...')
is_running = False
def handle_exit():
asyncio.ensure_future(stop_server())
loop.add_signal_handler(signal.SIGINT, handle_exit)
loop.run_until_complete(create_server())
#!/usr/bin/env python
import time
from datetime import datetime
import json
import logging
from socket import error as SocketError
import threading
from websocket_server import WebsocketServer
host = '127.0.0.1'
port = 32045
fps = 30
uri = "ws://%s:%s" % (host, port)
is_running = True
def get_data ():
# Here you could poll from hardware devices
data = {
'time': datetime.now()
}
return data
def message_loop(server):
global is_running
while is_running:
if server.clients:
data = get_data()
message = json.dumps(data, default=str)
for client in server.clients:
try:
server.send_message(client, message)
except SocketError as err:
print('Socket Error sending message to client, most likely closed')
except Exception as err:
print('Error sending message to client')
time.sleep(1.0 / fps)
server = WebsocketServer(port, host=host, loglevel=logging.WARNING)
print("""
Running Websocket Server on %s
""" % (uri))
message_thread = threading.Thread(target=message_loop, args=[server])
message_thread.start()
server.run_forever()
is_running = False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment