CyclingDave
CyclingDave

Reputation: 1476

How to send messages from server to client in a Python WebSockets server, AFTER initial handshake?

Here is a small websockets client and server POC. It sends a single hard-coded message string from the (Python) server to the Javascript client page.

The question is, how to send further, ad-hoc messages? From the server to the client.

Tiny HTML client page with embedded Javascript:

<!DOCTYPE html> 
<html lang="en">
<body> See console for messages </body>
<script>
# Create websocket
const socket = new WebSocket('ws://localhost:8000');

# Add listener to receive server messages
socket.addEventListener('open', function (event) {
    socket.send('Connection Established');
});

# Add message to browser console
socket.addEventListener('message', function (event) { 
    console.log(event.data);
});
 
</script>
</html>

Here is the Python server code:

import asyncio 
import websockets
import time 

# Create handler for each connection
async def handler(websocket, path):
    await websocket.send("message from websockets server")

# Start websocket server
start_server = websockets.serve(handler, "localhost", 8000)

# Start async code
asyncio.get_event_loop().run_until_complete(start_server) 
asyncio.get_event_loop().run_forever()

This successfully sends a hard-coded message from server to client. You can see the message in the browser console. At this point the websocket is open.

The main application (not shown) now needs to send messages. These will be dynamic messages, not hard-coded.

How can we send later, dynamic messages from the server? After the code here runs?

I would like to put the socket into a global variable and call a send method but this is not possible because the server runs a continuous loop.

Upvotes: 1

Views: 1962

Answers (1)

CyclingDave
CyclingDave

Reputation: 1476

You can insert further messages into the Python server code like this:


import asyncio
import datetime
from typing import Iterator
import websockets
import random

websocket_connections = set()
sock_port = 8000
sock_url = 'localhost'
global_socket = lambda: None

async def register(websocket):
    print('register event received')
    websocket_connections.add(websocket) # Add this client's socket
    global_socket = websocket

async def poll_log():
    await asyncio.sleep(0.3) # Settle
    while True:
        await asyncio.sleep(0.3) # Slow things down
        
        # Send a dynamic message to the client after random delay
        r = random.randint(1, 10)
        if (r == 5): # Only send 10% of the time
            a_msg = "srv -> cli: " + str(random.randint(1,10000))
            print("sending msg: " + a_msg)
            websockets.broadcast(websocket_connections, a_msg) # Send to all connected clients
        
async def main():
    sock_server = websockets.serve(register, sock_url, sock_port)
    await asyncio.sleep(0.3) # Start up time
    async with sock_server: await poll_log()

if __name__ == "__main__":
    print("Websockets server starting up ...")
    asyncio.run(main())

There is a very helpful example of a complete, full-duplex Websockets application here. That example is part of the Websockets 10.4 documentation. It's very helpful as a reference and to understand how websockets are used.

Upvotes: 2

Related Questions