I'm trying to create a unidirectional websocket connection between the client and server (python). The libraries I've currently been prototyping with are websockets and simplesocketserver. I've got a hello world example getting from the server to the client, but I need to be able to send data from the backend to the client unprompted from the client. All of the websockets examples seem to show the server listening to the client and then responding.
So far I've tried:
Using websockets 8.0, sending data from server to client unprompted, this works but only with hard-coded strings, I don't understand how to send real data on demand unprompted from the client
Using simplesocketserver in the same exact manner
Started investigating server sent events - is this more appropriate?
Example from the websockets documentation:
import asyncio
import websockets
async def hello(websocket, path):
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Note: the need for unidirectional communication is due to existing architecture.
Any guidance or resources on this would be great, I hope I'm overlooking something easy. Thank you!
I came across a similar issue. After some trial and error I found a solution that works for me, maybe so it does for you. I've tested this on Linux and with Firefox and mobile Safari (parallel).
The InputThread waits for input (words) on the command line and writes them into a list, that is sent to all connected clients each time a new string is appended or a client is connected.
Code snippet
import asyncio
import websockets
import json
from threading import Thread
words = []
clients = []
async def register_client(websocket, path):
# register new client in list and keep connection open
clients.append(websocket)
await send_to_all_clients()
while True:
await asyncio.sleep(10)
async def send_to_all_clients():
global words
for i, ws in list(enumerate(clients))[::-1]:
try:
await ws.send(json.dumps({"words": words}))
except websockets.exceptions.ConnectionClosedError:
# remove if connection closed
del clients[i]
class InputThread(Thread):
def run(self):
global words
async def sending_loop():
while True:
i = input()
if not i.strip():
continue
words.append({"options": i.strip().slit()})
await send_to_all_clients()
asyncio.run(sending_loop())
InputThread().start()
asyncio.get_event_loop().run_until_complete(
websockets.serve(register_client, "localhost", 8765))
asyncio.get_event_loop().run_forever()
Related
I am trying to test out if I send out multiple requests at the same moment using coroutine can cause the server side receives corrupted data.
My test is based on the sample code from: https://websockets.readthedocs.io/en/stable/intro.html
Somehow, for the following code, the server side only receive one requests? Anyone has some insights? thx
server (this is basically the same code from the websockets Getting Started webpage):
#!/usr/bin/env python3
# WS server example
import asyncio
import websockets
async def hello(websocket, path):
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Client, I created 1000 tasks, and schedule to run them as soon as possible:
#!/usr/bin/env python3
# WS client example
import asyncio
import websockets
uri = "ws://localhost:8765"
connection = None
async def hello():
global connection
name = "What's your name? "
await connection.send(name)
print(f"> {name}")
async def main():
global connection
connection = await websockets.connect(uri)
#asyncio.run(main())
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.run_until_complete(asyncio.wait(
[hello() for i in range(1000)], return_when=asyncio.ALL_COMPLETED
))
UPDATE
The solution is to use a loop.
I found the reason: the server side, handler should use a loop so that the corroutine will not finish immediately after received the first request.
The documentation you linked also includes this paragraph just below the server code:
On the server side, websockets executes the handler coroutine hello once for each WebSocket connection. It closes the connection when the handler coroutine returns.
The client code you linked creates one connection and sends messages on that connection. After the client sends the first message, the server closes the connection, so the next 999 messages you attempt to send are being sent on a closed connection.
If you update the hello handler to include a loop, you will see all messages.
import asyncio
import websockets
async def hello(websocket, path):
while True:
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
I am trying to create a code using rtmidi and websockets, which listenes for midi input and then sends data through websocket to all connected users. The data is beeing sent inside if statement, when some condition is met. In this test, the condition is simply random number comparison. Client can conenct to websocket server, however never receives any dat, even though server confirms that it was sent.
When I added asyncio.sleep(0) just before sending data, everything started working. Why is that? Should I use sleep, or is there a better solution?
I removed everything rtmidi related from example to make it easier, the problem is still the same.
Server code:
import asyncio
import websockets
import random
async def key(websocket, path):
print("port connected ", path)
while True:
rnd=random.randrange(0,300000)
if rnd==0:
#await asyncio.sleep(0) #when not commented everything works
print("sent")
await websocket.send("hello")
start_server = websockets.serve(key, "127.0.0.1", 8765)
print("done")
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Client code:
import asyncio
import websockets
async def gdata():
uri = "ws://127.0.0.1:8765"
async with websockets.connect(uri) as websocket:
while True:
data = await websocket.recv()
print(data)
asyncio.get_event_loop().run_until_complete(gdata())
I'm trying to build a multiplayer game (to be precise a Card game) in Python via websockets, but I'm currently failing in the very early steps.
What I'm trying to do:
Clients connect to the websocket, until a certain number is reached
The client sends an input to the server
The server responds to each client separately and simultaneously
What works:
Letting the client connect and storing a websocket per client works as expected, but then I'm a bit stuck.
What I've tried:
client.py
import asyncio
import websockets
import random
def random_name():
return "".join([random.choice("abcdefghijkl") for _ in range(5)])
async def main():
uri = "ws://localhost:1235"
print("Starting...")
async with websockets.connect(uri) as ws:
client_name = random_name()
await ws.send(client_name)
print(await ws.recv()) # server sends :client_name registered
print(await ws.recv()) # server sends: 3 people registered
print(await ws.recv()) # server sends: Waiting for input
inp = input()
await ws.send(inp)
asyncio.get_event_loop().run_until_complete(main())
server.py (very naive approach)
import websockets
import asyncio
clients = []
async def main(ws, *args, **kwargs):
while True:
print(f"Waiting for new connection! {ws}")
client_name = await ws.recv()
clients.append((client_name, ws, ws.remote_address))
await ws.send(f"{client_name} registered on {ws.remote_address}")
if len(clients) >= 3:
registered_clients = ', '.join([c[0] for c in clients])
print(f"3 clients ({registered_clients}) registered")
break
for name, ws_, _ in clients:
await ws_.send(f"3 clients registered ({registered_clients})")
await ws_.send(f"Waiting for input")
for _ in clients:
inp = await ws_.recv()
print(ws_.remote_address, inp)
start_server = websockets.serve(main, "localhost", "1235")
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
This fails with RuntimeError: cannot call recv while another coroutine is already waiting for the next message. It seems that I cannot wait for the answer of each connected clients individually. Even though not perfect, I thought I can distinguish each client by looking at their remote addresses. For doing so, I modified the main function to
server.py, main function (naive approach)
async def main(ws, *args, **kwargs):
while True:
print(f"Waiting for new connection! {ws}")
client_name = await ws.recv()
clients.append((client_name, ws, ws.remote_address))
await ws.send(f"{client_name} registered on {ws.remote_address}")
if len(clients) >= 3:
registered_clients = ', '.join([c[0] for c in clients])
print(f"3 clients ({registered_clients}) registered")
break
for name, ws_ in clients:
await ws_.send(f"3 clients registered ({registered_clients})")
await ws_.send(f"Waiting for input")
for _ in range(len(clients)):
inp = await ws.recv()
print(ws.remote_address, inp)
This doesn't work neither, cause the loop doesn't break for all connected clients.
My questions:
How can my examples be repaired?
Is the websockets package the right framework for me? Two things let me think of that:
In case of sockets, I can bind one client to the socket instance and directly read from one instance (which it seems I cannot easily do here).
Even though I'm pretty sure, that one can achieve what I want, it seems that there's a lot code involved for doing that (!?)
But somehow I'd like to stick to websockets in general (because of also having the possibility to connect to communicate wit a browser). And sticking to the websockets package in particular, would have the advantage for me to get to know the asyncio module better. But if someone says, that I should rather switch to aiohttp, flask-sockets, tornado etc. I'd like to do so as well.
I am looking at using websockets in my client (browser) - server (Python) application and I have a hard time to understand how to reuse a websocket channel across threads.
My application will have a few threads, each independently generating some data which I would like to push to the browser. The Getting Started examples make it clear that once a browser connects to the server, a specific function is ran (I copy the example from the linked docs):
import asyncio
import datetime
import random
import websockets
async def time(websocket, path):
while True:
now = datetime.datetime.utcnow().isoformat() + 'Z'
await websocket.send(now)
await asyncio.sleep(random.random() * 3)
start_server = websockets.serve(time, '127.0.0.1', 5678)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Now let's say I have a function (worker)
def thread1(an_existing_webocket):
while True:
an_existing_webocket.send("1")
print("1")
time.sleep(1)
which I would like to start via threading.Thread(target=thread1, args=(an_initialized_websocket,)).start().
How do these two worlds (websockets using coroutines and threading) coexist? And more specifically: what is the websocket I can pass to the workers so that they can use it to send messages upstream?
In other words, how should I create the an_initialized_websocket above?
Using Python v3.5 or v3.6 websocket clients are not closing when connected to some websocket servers. The example code below shows the process working correctly when connected to wss://echo.websocket.org but fails to disconnect when connecting to wss:/stream.pushbullet.com.
Can anyone see what's the difference? It hardly seems like it should have anything to do with the server and how it behaves (or possibly misbehaves).
import asyncio
import aiohttp
# Code: http://pastebin.com/G5sfpQG2
# Closing the echo.websocket.org connection works as expected
# Closing the stream.pushbullet.com connection hangs
async def run():
session = aiohttp.ClientSession()
API_KEY = "RrFnc1xaeQXnRrr2auoGA1e8pQ8MWmMF" # (OK to have here)
async with session.ws_connect('wss://stream.pushbullet.com/websocket/' + API_KEY) as ws:
# async with session.ws_connect("wss://echo.websocket.org") as ws:
ws.send_json({"hello": "world"})
async def _timeout():
await asyncio.sleep(2)
print('closing ... ', end="", flush=True)
await ws.close()
print('... closed. Should see "broke out of ..." messages next')
asyncio.get_event_loop().create_task(_timeout())
async for ws_msg in ws:
print("ws_msg:", ws_msg)
print("broke out of async for loop")
print("broke out of async with")
session.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
print("goodbye")
While the user has already answered the question, I have found the issue of servers not closing the socket, still prevalent on aiohttp 2.0 as well.
Many a times this is an issue with the server as well! After debugging it turns out some servers don't close ssl connections according to protocol. For such servers adding the following parameters while creating the connector object usually does the job.
force_close=True, enable_cleanup_closed=True
Adding this information for users who maybe still facing this issue on 2.0.
sigh Version 1.3.0 of aiohttp fixed this. I'll assume it was a bug. I was on v1.2.0.
-Rob