Quick start - websockets 10.3 documentation
Description: Here are a few examples to get you started quickly with websockets. Say “Hello world!”: Here’s a WebSocket server. It receives a name from the client, sends a greeting, and closes the connection. s...

Hide navigation sidebar
Hide table of contents sidebar
Back to top
Toggle table of contents sidebar

# Quick start

Here are a few examples to get you started quickly with websockets.

## Say “Hello world!”

Here’s a WebSocket server.

It receives a name from the client, sends a greeting, and closes the connection.

server.py

     1#!/usr/bin/env python
     2
     3import asyncio
     4import websockets
     5
     6async def hello(websocket):
     7    name = await websocket.recv()
     8    print(f"<<< {name}")
     9
    10    greeting = f"Hello {name}!"
    11
    12    await websocket.send(greeting)
    13    print(f">>> {greeting}")
    14
    15async def main():
    16    async with websockets.serve(hello, "localhost", 8765):
    17        await asyncio.Future()  # run forever
    18
    19if __name__ == "__main__":
    20    asyncio.run(main())

`serve()` executes the connection handler coroutine `hello()` once for each WebSocket connection. It closes the WebSocket connection when the handler returns.

Here’s a corresponding WebSocket client.

It sends a name to the server, receives a greeting, and closes the connection.

client.py

     1#!/usr/bin/env python
     2
     3import asyncio
     4import websockets
     5
     6async def hello():
     7    uri = "ws://localhost:8765"
     8    async with websockets.connect(uri) as websocket:
     9        name = input("What's your name? ")
    10
    11        await websocket.send(name)
    12        print(f">>> {name}")
    13
    14        greeting = await websocket.recv()
    15        print(f"<<< {greeting}")
    16
    17if __name__ == "__main__":
    18    asyncio.run(hello())

Using `connect()` as an asynchronous context manager ensures the WebSocket connection is closed.

## Encrypt connections

Secure WebSocket connections improve confidentiality and also reliability because they reduce the risk of interference by bad proxies.

The `wss` protocol is to `ws` what `https` is to `http`. The connection is encrypted with TLS (Transport Layer Security). `wss` requires certificates like `https`.

TLS vs. SSL

TLS is sometimes referred to as SSL (Secure Sockets Layer). SSL was an earlier encryption protocol; the name stuck.

Here’s how to adapt the server to encrypt connections. You must download `localhost.pem` and save it in the same directory as `server_secure.py`.

See the documentation of the `ssl` module for details on configuring the TLS context securely.

server_secure.py

     1#!/usr/bin/env python
     2
     3import asyncio
     4import pathlib
     5import ssl
     6import websockets
     7
     8async def hello(websocket):
     9    name = await websocket.recv()
    10    print(f"<<< {name}")
    11
    12    greeting = f"Hello {name}!"
    13
    14    await websocket.send(greeting)
    15    print(f">>> {greeting}")
    16
    17ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    18localhost_pem = pathlib.Path(__file__).with_name("localhost.pem")
    19ssl_context.load_cert_chain(localhost_pem)
    20
    21async def main():
    22    async with websockets.serve(hello, "localhost", 8765, ssl=ssl_context):
    23        await asyncio.Future()  # run forever
    24
    25if __name__ == "__main__":
    26    asyncio.run(main())

Here’s how to adapt the client similarly.

client_secure.py

     1#!/usr/bin/env python
     2
     3import asyncio
     4import pathlib
     5import ssl
     6import websockets
     7
     8ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
     9localhost_pem = pathlib.Path(__file__).with_name("localhost.pem")
    10ssl_context.load_verify_locations(localhost_pem)
    11
    12async def hello():
    13    uri = "wss://localhost:8765"
    14    async with websockets.connect(uri, ssl=ssl_context) as websocket:
    15        name = input("What's your name? ")
    16
    17        await websocket.send(name)
    18        print(f">>> {name}")
    19
    20        greeting = await websocket.recv()
    21        print(f"<<< {greeting}")
    22
    23if __name__ == "__main__":
    24    asyncio.run(hello())

In this example, the client needs a TLS context because the server uses a self-signed certificate.

When connecting to a secure WebSocket server with a valid certificate — any certificate signed by a CA that your Python installation trusts — you can simply pass `ssl=True` to `connect()`.

## Connect from a browser

The WebSocket protocol was invented for the web — as the name says!

Here’s how to connect to a WebSocket server from a browser.

Run this script in a console:

show_time.py

     1#!/usr/bin/env python
     2
     3import asyncio
     4import datetime
     5import random
     6import websockets
     7
     8async def show_time(websocket):
     9    while True:
    10        message = datetime.datetime.utcnow().isoformat() + "Z"
    11        await websocket.send(message)
    12        await asyncio.sleep(random.random() * 2 + 1)
    13
    14async def main():
    15    async with websockets.serve(show_time, "localhost", 5678):
    16        await asyncio.Future()  # run forever
    17
    18if __name__ == "__main__":
    19    asyncio.run(main())

Save this file as `show_time.html`:

show_time.html

    1<!DOCTYPE html>
    2<html lang="en">
    3    <head>
    4        <title>WebSocket demo</title>
    5    </head>
    6    <body>
    7        <script src="show_time.js"></script>
    8    </body>
    9</html>

Save this file as `show_time.js`:

show_time.js

     1window.addEventListener("DOMContentLoaded", () => {
     2  const messages = document.createElement("ul");
     3  document.body.appendChild(messages);
     4
     5  const websocket = new WebSocket("ws://localhost:5678/");
     6  websocket.onmessage = ({ data }) => {
     7    const message = document.createElement("li");
     8    const content = document.createTextNode(data);
     9    message.appendChild(content);
    10    messages.appendChild(message);
    11  };
    12});

Then, open `show_time.html` in several browsers. Clocks tick irregularly.

## Broadcast messages

Let’s change the previous example to send the same timestamps to all browsers, instead of generating independent sequences for each client.

Stop the previous script if it’s still running and run this script in a console:

show_time_2.py

     1#!/usr/bin/env python
     2
     3import asyncio
     4import datetime
     5import random
     6import websockets
     7
     8CONNECTIONS = set()
     9
    10async def register(websocket):
    11    CONNECTIONS.add(websocket)
    12    try:
    13        await websocket.wait_closed()
    14    finally:
    15        CONNECTIONS.remove(websocket)
    16
    17async def show_time():
    18    while True:
    19        message = datetime.datetime.utcnow().isoformat() + "Z"
    20        websockets.broadcast(CONNECTIONS, message)
    21        await asyncio.sleep(random.random() * 2 + 1)
    22
    23async def main():
    24    async with websockets.serve(register, "localhost", 5678):
    25        await show_time()
    26
    27if __name__ == "__main__":
    28    asyncio.run(main())

Refresh `show_time.html` in all browsers. Clocks tick in sync.

## Manage application state

A WebSocket server can receive events from clients, process them to update the application state, and broadcast the updated state to all connected clients.

Here’s an example where any client can increment or decrement a counter. The concurrency model of `asyncio` guarantees that updates are serialized.

Run this script in a console:

counter.py

     1#!/usr/bin/env python
     2
     3import asyncio
     4import json
     5import logging
     6import websockets
     7
     8logging.basicConfig()
     9
    10USERS = set()
    11
    12VALUE = 0
    13
    14def users_event():
    15    return json.dumps({"type": "users", "count": len(USERS)})
    16
    17def value_event():
    18    return json.dumps({"type": "value", "value": VALUE})
    19
    20async def counter(websocket):
    21    global USERS, VALUE
    22    try:
    23        # Register user
    24        USERS.add(websocket)
    25        websockets.broadcast(USERS, users_event())
    26        # Send current state to user
    27        await websocket.send(value_event())
    28        # Manage state changes
    29        async for message in websocket:
    30            event = json.loads(message)
    31            if event["action"] == "minus":
    32                VALUE -= 1
    33                websockets.broadcast(USERS, value_event())
    34            elif event["action"] == "plus":
    35                VALUE += 1
    36                websockets.broadcast(USERS, value_event())
    37            else:
    38                logging.error("unsupported event: %s", event)
    39    finally:
    40        # Unregister user
    41        USERS.remove(websocket)
    42        websockets.broadcast(USERS, users_event())
    43
    44async def main():
    45    async with websockets.serve(counter, "localhost", 6789):
    46        await asyncio.Future()  # run forever
    47
    48if __name__ == "__main__":
    49    asyncio.run(main())

Save this file as `counter.html`:

counter.html

     1<!DOCTYPE html>
     2<html lang="en">
     3    <head>
     4        <title>WebSocket demo</title>
     5        <link href="counter.css" rel="stylesheet">
     6    </head>
     7    <body>
     8        <div class="buttons">
     9            <div class="minus button">-</div>
    10            <div class="value">?</div>
    11            <div class="plus button">+</div>
    12        </div>
    13        <div class="state">
    14            <span class="users">?</span> online
    15        </div>
    16        <script src="counter.js"></script>
    17    </body>
    18</html>

Save this file as `counter.css`:

counter.css

     1body {
     2    font-family: "Courier New", sans-serif;
     3    text-align: center;
     4}
     5.buttons {
     6    font-size: 4em;
     7    display: flex;
     8    justify-content: center;
     9}
    10.button, .value {
    11    line-height: 1;
    12    padding: 2rem;
    13    margin: 2rem;
    14    border: medium solid;
    15    min-height: 1em;
    16    min-width: 1em;
    17}
    18.button {
    19    cursor: pointer;
    20    user-select: none;
    21}
    22.minus {
    23    color: red;
    24}
    25.plus {
    26    color: green;
    27}
    28.value {
    29    min-width: 2em;
    30}
    31.state {
    32    font-size: 2em;
    33}

Save this file as `counter.js`:

counter.js

     1window.addEventListener("DOMContentLoaded", () => {
     2  const websocket = new WebSocket("ws://localhost:6789/");
     3
     4  document.querySelector(".minus").addEventListener("click", () => {
     5    websocket.send(JSON.stringify({ action: "minus" }));
     6  });
     7
     8  document.querySelector(".plus").addEventListener("click", () => {
     9    websocket.send(JSON.stringify({ action: "plus" }));
    10  });
    11
    12  websocket.onmessage = ({ data }) => {
    13    const event = JSON.parse(data);
    14    switch (event.type) {
    15      case "value":
    16        document.querySelector(".value").textContent = event.value;
    17        break;
    18      case "users":
    19        const users = `${event.count} user${event.count == 1 ? "" : "s"}`;
    20        document.querySelector(".users").textContent = users;
    21        break;
    22      default:
    23        console.error("unsupported event", event);
    24    }
    25  };
    26});

Then open `counter.html` file in several browsers and play with [+] and [-].