Web Sockets in Python

Introduction

We can implement WebSocket servers and clients in Python using the websockets library.

According to the websockets documentation:

websockets is a library for building WebSocket servers and clients in Python with a focus on correctness, simplicity, robustness, and performance.

It supports several network I/O and control flow paradigms:

  1. The default implementation builds upon asyncio, Python’s standard asynchronous I/O framework. It provides an elegant coroutine-based API. It’s ideal for servers that handle many clients concurrently.
  2. The threading implementation is a good alternative for clients, especially if you aren’t familiar with asyncio. It may also be used for servers that don’t need to serve many clients.
  3. The Sans-I/O implementation is designed for integrating in third-party libraries, typically application servers, in addition being used internally by websockets.

Install Python Socket

It's on the Python Package Index, so just use pip:

pip install websockets

Python WebSocket Server

Using the socket module, a server can be created where the server will listen for incoming connections from clients. The incoming connections will establish long-lived connections between the client and the server through a socket.

Then the server establishes an indefinite loop to keep sending updates to the client.

# ws-stream-server.py
import socket
import time
HEADER = 10

ws = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ws.bind((socket.gethostname(), 9092))
ws.listen(5)

while True:
(clientsocket, address) = ws.accept()
print(f"Connect from {address} established")

msg = "Hello From Server- You are Connected"
msg = f"{len(msg):<{HEADER}}"+msg

#clientsocket.send(bytes(msg, 'utf-8'))
# start pushing messages every 3 seconds
for i in range(20):
time.sleep(0)
msg = f"The time is {time.time()}"
msg = f'{len(msg):<{HEADER}}' + msg # what is this doing?
clientsocket.send(bytes(msg, 'utf-8'))
print(msg)

Python WebSocket Client

# ws-stream-client.py
import socket
HEADER = 10

ws = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ws.connect((socket.gethostname(), 9092))

while True:
full_msg = ''
new_msg = True
while True:
msg = ws.recv(16).decode('utf-8')
#print(f"got: {msg}")
if new_msg:
# get length from Header
tmp_msg = full_msg + msg
msg_len = int(tmp_msg[:HEADER])
new_msg = False

full_msg += msg
if len(full_msg) >= msg_len + HEADER:
total_msg_len = msg_len + HEADER
print(f"M:{full_msg[0:total_msg_len]}")
next = full_msg[total_msg_len:]
full_msg = next
new_msg = True

Note how this message handling allows for messages to be split up for exceeding the allowed number of characters per message. This is done by examining the length of the message and if the whole thing is contained by the incoming msg buffer. If it is not, it appends full_msg with the incoming msg on the next iteration of the loop. These sorts of chunking of data is going to be necessary to manage the messages being sent.

References

Web Links

Note Links