Last active
July 16, 2024 14:18
-
-
Save CardealRusso/b651a081a5735d58025670ea8f2f4159 to your computer and use it in GitHub Desktop.
simple micropython unsafe (ws) multithreaded websocket server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import binascii, socket, _thread, hashlib | |
class WebSocketServer: | |
def __init__(self, host='0.0.0.0', port=8443, on_connect=None, on_disconnect=None, on_message=None): | |
self.host = host | |
self.port = port | |
self.clients = [] | |
self.on_connect = on_connect | |
self.on_disconnect = on_disconnect | |
self.on_message = on_message | |
def generate_accept(self, key): | |
return binascii.b2a_base64(hashlib.sha1(key + b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest()).strip() | |
def decode_frame(self, frame): | |
length = frame[1] & 127 | |
mask = frame[2:6] if length < 126 else frame[4:8] | |
data = frame[6:] if length < 126 else frame[8:] | |
return ''.join(chr(b ^ mask[i % 4]) for i, b in enumerate(data)) | |
def encode_frame(self, msg): | |
msg_bytes = msg.encode('utf-8') | |
length = len(msg_bytes) | |
frame = bytearray([129]) | |
if length <= 125: | |
frame.append(length) | |
elif length <= 65535: | |
frame.extend([126, (length >> 8) & 255, length & 255]) | |
else: | |
frame.extend([127, 0, 0, 0, 0, 0, (length >> 24) & 255, (length >> 16) & 255, (length >> 8) & 255, length & 255]) | |
frame.extend(msg_bytes) | |
return frame | |
def close(self, client_s): | |
self.clients.remove(client_s) | |
client_s.close() | |
#Perhaps it would be appropriate to pick up the client thread and close it? I don't know if that's necessary, maybe it does it by itself. | |
def handle_client(self, client_s, addr): | |
ip_addr = '.'.join(map(str, addr[4:8])) | |
#client_s will spit out errors if the client is connecting through a secure connection. | |
method, path, protocol = client_s.readline().decode().strip().split() | |
headers = {} | |
while True: | |
line = client_s.readline().decode().strip() | |
if not line: | |
break | |
if ": " in line: | |
key, value = line.split(": ", 1) | |
headers[key] = value | |
accept = self.generate_accept(headers["Sec-WebSocket-Key"].encode('utf-8')) | |
client_s.send(f'HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {accept.decode()}\r\n\r\n'.encode()) | |
self.clients.append(client_s) | |
if self.on_connect:self.on_connect(client_s, ip_addr) | |
try: | |
while True: | |
#This will mess current and subsequent messages if the current one exceeds the limit of 1024 | |
data = client_s.recv(1024) | |
if not data:break | |
if self.on_message:self.on_message(client_s, ip_addr, self.decode_frame(data)) | |
finally: | |
self.close(client_s) | |
if self.on_disconnect:self.on_disconnect(client_s, ip_addr) | |
def start(self): | |
s = socket.socket() | |
ai = socket.getaddrinfo(self.host, self.port) | |
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
s.bind(ai[0][-1]) | |
s.listen(0) | |
while True:_thread.start_new_thread(self.handle_client, (s.accept())) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from unws import WebSocketServer | |
def on_connect(client, ip): | |
print(f'New client: {ip}') | |
def on_disconnect(client, ip): | |
print(f'{ip} left') | |
def on_message(client, ip, message): | |
print(f'{ip} : {message}') | |
if message == "Send this to everyone": | |
for clientp in ws.clients: | |
clientp.send(ws.encode_frame("this")) | |
if message == "bye": | |
client.send(ws.encode_frame("Goodbye, despite the fact that I don't know if you'll get this message before disconnect.") | |
ws.close(client) | |
ws = WebSocketServer(host="0.0.0.0", port=8080, on_connect=on_connect, on_disconnect=on_disconnect, on_message=on_message) | |
ws.start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment