Created
April 10, 2020 18:32
-
-
Save ansemjo/eccdfb7a5c60b408d51afec0affe046e to your computer and use it in GitHub Desktop.
an example of a vanilla WebSocket-based chat with Python's aiohttp 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
from aiohttp import web | |
import os, binascii | |
import asyncio | |
import weakref | |
import datetime | |
app = web.Application() | |
app["ws"] = weakref.WeakSet() | |
routes = web.RouteTableDef() | |
@routes.get("/ws") | |
async def wshandler(request): | |
# open new connection | |
ws = web.WebSocketResponse() | |
await ws.prepare(request) | |
# send id and insert in set | |
i = binascii.hexlify(os.urandom(2)).decode() | |
ws["id"] = i | |
await ws.send_json(dict(type="id", id=i)) | |
request.app["ws"].add(ws) | |
# handle messages | |
try: | |
async for msg in ws: | |
print(f"received from {i}: {msg.data}") | |
await broadcast(request.app, i, msg.data) | |
finally: | |
request.app["ws"].discard(ws) | |
return ws | |
@routes.get("/") | |
async def hello(request): | |
response = web.Response(content_type="text/html") | |
response.text = """ | |
<!doctype html> | |
<html> | |
<head> | |
<title> SimpleSocketChat </title> | |
<style> | |
table { border-collapse: collapse; margin-top: 1em; width: 100%; } | |
th { background-color: skyblue; } | |
td, th { padding: 0.5em; } | |
</style> | |
</head> | |
<body> | |
<h1> SimpleSocketChat </h1> | |
<p>Current time: <code id="now">unknown</code></p> | |
<p><code id="status">unknown status</code></p> | |
<form id="chat" onsubmit="send(this); return false;"> | |
<input type="text" id="message" style="width: 300px"> | |
<input type="submit" value="Send"> | |
</form> | |
<table border="1"> | |
<tr> | |
<th>ID</th> | |
<th>Date</th> | |
<th>Message</th> | |
</tr> | |
<tbody id="messages"> | |
</tbody> | |
</table> | |
<script> | |
ws = new WebSocket(`ws://${location.host}/ws`); | |
ws.addEventListener("message", function(event) { | |
let msg = JSON.parse(event.data); | |
switch(msg.type) { | |
case "id": | |
let st = document.getElementById("status"); | |
st.textContent = `CONNECTED as ${msg.id}`; | |
break; | |
case "now": | |
let now = document.getElementById("now"); | |
now.textContent = msg.now; | |
break; | |
case "message": | |
let messages = document.getElementById("messages"); | |
let m = document.createElement("tr"); | |
[msg.id, msg.date, msg.message].forEach(t => { | |
let td = document.createElement("td"); | |
td.textContent = t; | |
m.append(td); | |
}); | |
messages.prepend(m); | |
break; | |
}; | |
}); | |
ws.addEventListener("close", () => { | |
st = document.getElementById("status"); | |
st.textContent = "CONNECTION CLOSED"; | |
}); | |
function send(el) { | |
message = el["message"].value; | |
ws.send(message); | |
el["message"].value = ""; | |
}; | |
</script> | |
</body> | |
</html> | |
""" | |
return response | |
async def currenttime(app): | |
while True: | |
now = datetime.datetime.utcnow().isoformat() | |
for ws in app["ws"]: | |
await ws.send_json(dict(type="now", now=now)) | |
await asyncio.sleep(1) | |
async def broadcast(app, sender, message): | |
now = datetime.datetime.utcnow().isoformat() | |
for ws in app["ws"]: | |
await ws.send_json(dict(type="message", id=sender, date=now, message=message)) | |
async def connectedclients(app): | |
clients = dict(type="clients", clients=[hex(ws["id"]) for ws in app["ws"]]) | |
for ws in app["ws"]: | |
await ws.send_json(clients) | |
async def start_background_tasks(app): | |
app["timer"] = asyncio.create_task(currenttime(app)) | |
app.add_routes(routes) | |
app.on_startup.append(start_background_tasks) | |
web.run_app(app) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Preview