I asked these questions about websockets in sanic. This is a follow up.
The following lets me broadcast messages to my websocket clients:
from sanic import Sanic, response
from sanic.websocket import WebSocketProtocol
import asyncio
import time
app = Sanic()
@app.websocket('/feed')
async def feed(request, ws):
while True:
now = time.time()
data = f'message: {now}'
print(data)
await ws.send(data)
await asyncio.sleep(1)
@app.route('/')
async def handle_request(request):
return response.html("""
<html><head>
<script>
const ws = new WebSocket("ws://" + location.host + '/feed');
ws.onmessage = event => {
let e = event;
console.log(e.data);
let out = document.getElementById('out');
out.innerHTML += `<li><p>${e.data}</p></li>`;
}
document.querySelector('form').addEventListener('submit', (event) => {
event.preventDefault();
let message = document.querySelector("#in").value;
ws.send(message);
document.querySelector("#in").value = "";
})
</script>
</head>
<body><h1>Main</h1>
<div id="in"><form><input type="text" method="post"></form></div>
<div><ul id="out"></ul></div>
</body>
</html>
""")
app.run(host="0.0.0.0", port=8000)
I can confirm that the server is both periodically delivering messages to the client, and able to receive some kind of message back.
On the server:
[2019-08-12 22:06:27 -0500] - (sanic.access)[INFO][127.0.0.1:49028]: GET http://localhost:8000/ 200 714
message: 1565665587.4367297
message: 1565665588.4373734
message: 1565665589.4389973
message: 1565665590.440603
message: 1565665591.4414358
message: 1565665592.441888
message: 1565665593.443465
[2019-08-12 22:06:34 -0500] - (sanic.access)[INFO][127.0.0.1:49036]: GET http://localhost:8000/ 200 714
message: 1565665594.362771
message: 1565665595.3643198
message: 1565665596.3655813
message: 1565665597.3671694
And on the client:
However, this doesn't make sense to me for several reasons:
- When I submit the form, I can't actually see the contents of the submission, just that there was a GET request.
- I see a GET request, but my form explicitly says POST.
event.preventDefault()is used, and I'm still seeing a full page-refresh on the form submission.
But moreover, what I really want is bi-directional communication. So, in addition to just ws.send(data), I am going to new = await ws.recv() in the method and capture the input.
@app.websocket('/feed')
async def feed(request, ws):
while True:
now = time.time()
data = f'message: {now}'
print(data)
await ws.send(data)
new = await ws.recv() # this is the only change
print(new)
await asyncio.sleep(1)
But now, I no longer continuously send data to the client. Instead, the process hangs, waiting for my ws.recv() even when the client hasn't sent anything.
[2019-08-12 22:13:52 -0500] [12920] [INFO] Starting worker [12920]
[2019-08-12 22:13:56 -0500] - (sanic.access)[INFO][127.0.0.1:49086]: GET http://localhost:8000/ 200 714
message: 1565666037.0688074
[2019-08-12 22:14:03 -0500] - (sanic.access)[INFO][127.0.0.1:49090]: GET http://localhost:8000/ 200 714
And no more message is sent until I submit the form again.
How can I have bi-directional communication, passing data back into the /feed endpoint, but continuously without waiting?
