In Python, I'm using "websockets" library for websocket client.
import asyncio
import websockets
async def init_sma_ws():
uri = "wss://echo.websocket.org/"
async with websockets.connect(uri) as websocket:
name = input("What's your name? ")
await websocket.send('name')
greeting = await websocket.recv()
The problem is the client websocket connection is disconnected once a response is received. I want the connection to remain open so that I can send and receive messages later.
What changes do I need to keep the websocket open and be able to send and receive messages later?
I think your websocket is disconnected due to exit from context manager after recv().
Such code works perfectly:
import asyncio
import websockets
async def init_sma_ws():
uri = "wss://echo.websocket.org/"
async with websockets.connect(uri) as websocket:
while True:
name = input("What's your name? ")
if name == 'exit':
break
await websocket.send(name)
print('Response:', await websocket.recv())
asyncio.run(init_sma_ws())
In your approach you used a asynchronous context manager which closes a connection when code in the block is executed. In the example below an infinite asynchronous iterator is used which keeps the connection open.
import asyncio
import websockets
async def main():
async for websocket in websockets.connect(...):
try:
...
except websockets.ConnectionClosed:
continue
asyncio.run(main())
More info in library's docs.
Related
I have the following Python websocket server. It gets some usage data from my server and then sends it through a websocket connection:
#!/usr/bin/env python3
import json
import asyncio
import websockets
import websockets.server
import data_parser as dp
async def handler(websocket: websockets.server.WebSocketServerProtocol):
"""
Handles data connection and dispatches necessary data
"""
# Receive and parse the amount of days wanted
message = await websocket.recv()
json_message = json.loads(message)
days = json_message["days"]
for i, data in enumerate(dp.get_upto_nth_usage_data(days)):
await websocket.send(json.dumps(data, separators=(",", ":")))
to_send = {"finished": 1}
await websocket.send(json.dumps(to_send, separators=(",", ":")))
async def main():
async with websockets.serve(handler, "", 8001):
await asyncio.Future() # run forever
if __name__ == "__main__":
asyncio.run(main())
The problem is that I have to send large chunks of data(inside the for loop) over each websocket connection and when a new user tries to make a connection, he has to wait for the last user to receive all his data to be able to begin receiving his own. Is there a way to open a new port to handle new websocket connections or should I use another kind of protocol to send data (and if so, which would be the best choice)?
I'm new to websockets and asyncio and I'm trying to get a simple example working. I would like to create a server that accepts connections from multiple clients and concurrently runs a loop that sends a message to every connection once a second. I'm also trying to use asyncio.run() which I believe is preferred to the get_event_loop() code on which many examples are based.
Here's my code so far:
import asyncio
import websockets
USERS = set()
async def register(websocket, path):
USERS.add(websocket)
await websocket.send("Successfully registered")
async def count():
count = 0
while True:
print(f"Iteration: {count}")
if USERS:
for user in USERS:
await user.send("Sending message back to client")
await asyncio.sleep(1)
count +=1
async def serve():
server = await websockets.serve(register, 'localhost', 8765)
await server.wait_closed()
print("Server closed")
async def main():
await asyncio.gather(count(), serve())
asyncio.run(main())
When I run this the count coroutine works until I make a connection from a client. At this point the connection is successfully registered but when I try to send a message back to the client in count() I get an error because the connection is already closed. How should I change my code to stop this from happening?
How should I change my code to stop this from happening?
The problem might be that your handler, the register coroutine, is returning immediately, which prompts websockets to close the connection. Try to change it like this:
async def register(websocket, path):
USERS.add(websocket)
await websocket.send("Successfully registered")
await asyncio.Event().wait()
If that helps, you can put the event in USERS along with websocket, so that you have a way to terminate the connection to the client when you want to.
I am trying to messing up with Websockets module and after checking the main page:
https://websockets.readthedocs.io/en/stable/intro.html
I did following:
SERVER
# SERVER
import asyncio
import websockets
import nest_asyncio
USERS = {}
async def set_online(websocket, user_name):
USERS[user_name] = websocket
await notify()
async def set_offline(websocket, user_name):
USERS.pop(user_name, None)
await notify()
async def notify():
if USERS:
message = "Online users: {}\n".format(len(USERS))
print (message)
#await asyncio.wait([user.send(message) for user in USERS])
else:
message = "Online users: 0\n"
print (message)
async def server(websocket, path):
user_name = await websocket.recv()
await set_online(websocket, user_name)
try:
async for message in websocket:
for user_name, user_ws in USERS.items():
if websocket == user_ws:
print (f"{user_name}: {message}")
finally:
await set_offline(websocket, user_name)
start_server = websockets.serve(server, "localhost", 3000,
ping_interval=None)
nest_asyncio.apply()
loop = asyncio.get_event_loop()
loop.run_until_complete(start_server)
loop.run_forever()
and also:
CLIENT
# CLIENT
import asyncio
import websockets
import nest_asyncio
async def client(localhost, port):
uri = "ws://{0}:{1}".format(localhost, str(port))
async with websockets.connect(uri) as websocket:
user_name = input("set your name: ")
await websocket.send(f"{user_name}")
while True:
message = input("> ")
if message == "/quit":
break
else:
await websocket.send(message)
host = "localhost"
port = 3000
nest_asyncio.apply()
loop = asyncio.get_event_loop()
loop.run_until_complete(client(host, port))
so all works as expected but I would like to achieve that each user can receive the answer as well from other users.
I found there is a conflict when I want to use websocket.send(message) in for loop async for message in websocket: on SERVER side
The link which I paste above, I think has a solution but I am struggling to figure out how to use it properly in my script.
I believe I need to create two tasks (send and recv) which will work in parallel.
Like:
async def handler(websocket, path):
consumer_task = asyncio.ensure_future(consumer_handler(websocket, path))
producer_task = asyncio.ensure_future(producer_handler(websocket, path))
done, pending = await asyncio.wait([consumer_task, producer_task],return_when=asyncio.FIRST_COMPLETED)
for task in pending:
task.cancel()
the following is displayed on the website which I provided above, just one thing needs to be changed from asyncio.ensure_future to asyncio.create_task. I implemented function handler, producer, consumer, producer_handler and consumer_handler to make it works but no luck.
Could someone provide an example or how this should be set up correctly?
I believe asyncio.create_task should be used on both (SERVER and CLIENT) so they both receive and send at one time.
This is pretty long but I hope someone can help me with it and also maybe my part of script will be handy for someone as well!
I want to send values from a for loop from the client-server but the server only receives the first value and the connection is cut shortly
Client
import asyncio
import websockets
import time
async def message():
async with websockets.connect("ws://-------:5051") as socket:
for i in range(20):
await socket.send(f"{i}")
print(i)
time.sleep(4)
asyncio.get_event_loop().run_until_complete(message())
Server
import asyncio
import websockets
async def consumer_handler(websocket,path):
client_type = await websocket.recv()
print(client_type)
start_server = websockets.serve(consumer_handler,"ws://-------:5051", 5051)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
So, your consumer_handler receives message once and finishes.
You need to add loop.
Try something like this:
async def consumer_handler(websocket, path):
async for msg in websocket:
print(msg)
I am trying to establish a Web-Socket connection to a server and enter into receive mode.Once the client starts receiving the data, it immediately closes the connection with below exception
webSoc_Received = await websocket.recv()
File "/root/envname/lib/python3.6/site-packages/websockets/protocol.py", line 319, in recv
raise ConnectionClosed(self.close_code, self.close_reason)
websockets.exceptions.ConnectionClosed: WebSocket connection is closed: code = 1007, no reason.
Client-side Code Snippet :
import asyncio
import websockets
async def connect_ws():
print("websockets.client module defines a simple WebSocket client API::::::")
async with websockets.client.connect(full_url,extra_headers=headers_conn1) as websocket:
print ("starting")
webSoc_Received = await websocket.recv()
print ("Ending")
Decode_data = zlib.decompress(webSoc_Received)
print(Decode_data)
asyncio.get_event_loop().run_until_complete(connect_ws())
Any thoughts on this?
You use run_until_complete() which completes once you started process. Instead, you should use .run_forever(). It will keep your socket open, until you close it.
EDIT:
You can do something like this:
loop = asyncio.get_event_loop()
loop.call_soon(connect_ws) # Calls connect_ws once the event loop starts
loop.run_forever()
Or:
loop = asyncio.get_event_loop()
loop.run_until_complete(connect_ws())
loop.run_forever()
Or if previous examples didn't succeed, you can try with following code:
import asyncio
#asyncio.coroutine
def periodic():
while True:
print("websockets.client module defines a simple WebSocket client API::::::")
with websockets.client.connect(full_url,extra_headers=headers_conn1) as websocket:
print ("starting")
webSoc_Received = websocket.recv()
print ("Ending")
Decode_data = zlib.decompress(webSoc_Received)
print(Decode_data)
def stop():
task.cancel()
task = asyncio.Task(periodic())
loop = asyncio.get_event_loop()
loop.call_later(5, stop)
try:
loop.run_until_complete(task)
except asyncio.CancelledError:
pass
From what I can tell, your current code will exit after it receives its first message.
Try changing your code to a consumer pattern, as mentioned in the websocket docs here:
https://websockets.readthedocs.io/en/stable/intro.html#common-patterns
import asyncio
import websockets
async def connect_ws():
print("websockets.client module defines a simple WebSocket client API::::::")
async with websockets.client.connect(full_url,extra_headers=headers_conn1) as websocket:
while True:
print ("starting")
webSoc_Received = await websocket.recv()
print ("Ending")
Decode_data = zlib.decompress(webSoc_Received)
print(Decode_data)