Websocket getting closed immediately after connecting to FastAPI Endpoint - python

I'm trying to connect a websocket aiohttp client to a fastapi websocket endpoint, but I can't send or recieve any data because it seems that the websocket gets closed immediately after connecting to the endpoint.
server
import uvicorn
from fastapi import FastAPI, WebSocket
app = FastAPI()
#app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
...
if __name__ == '__main__':
uvicorn.run('test:app', debug=True, reload=True)
client
import aiohttp
import asyncio
async def main():
s = aiohttp.ClientSession()
ws = await s.ws_connect('ws://localhost:8000/ws')
while True:
...
asyncio.run(main())
When I try to send data from the server to the client when a connection is made
server
#app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
await websocket.send_text('yo')
client
while True:
print(await ws.receive())
I always get printed in my client's console
WSMessage(type=<WSMsgType.CLOSED: 257>, data=None, extra=None)
While in the server's debug console it says
INFO: ('127.0.0.1', 59792) - "WebSocket /ws" [accepted]
INFO: connection open
INFO: connection closed
When I try to send data from the client to the server
server
#app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
await websocket.receive_text()
client
ws = await s.ws_connect('ws://localhost:8000/ws')
await ws.send_str('client!')
Nothing happens, I get no message printed out in the server's console, just the debug message saying the client got accepted, connection opened and closed again.
I have no idea what I'm doing wrong, I followed this tutorial in the fastAPI docs for a websocket and the example there with the js websocket works completely fine.

The connection is closed by either end (client or server), as shown from your code snippets. You would need to have a loop in both the server and the client for being able to await for messages, as well as send messages, continuously (have a look here and here).
Additionally, as per FastAPI's documentation:
When a WebSocket connection is closed, the await websocket.receive_text() will raise a WebSocketDisconnect
exception, which you can then catch and handle like in this example.
Thus, on server side, you should use a try-except block to catch and handle WebSocketDisconnect exceptions. Below is a working example demonstrating a client (in aiohttp) - server (in FastAPI) communication using websockets.
Working Example
Server
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import uvicorn
app = FastAPI()
#app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
# await for connections
await websocket.accept()
try:
# send "Connection established" message to client
await websocket.send_text("Connection established!")
# await for messages and send messages
while True:
msg = await websocket.receive_text()
if msg.lower() == "close":
await websocket.close()
break
else:
print(f'CLIENT says - {msg}')
await websocket.send_text(f"Your message was: {msg}")
except WebSocketDisconnect:
print("Client disconnected")
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
Client
import aiohttp
import asyncio
async def main():
async with aiohttp.ClientSession() as session:
async with session.ws_connect('ws://127.0.0.1:8000/ws') as ws:
# await for messages and send messages
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
print(f'SERVER says - {msg.data}')
text = input('Enter a message: ')
await ws.send_str(text)
elif msg.type == aiohttp.WSMsgType.ERROR:
break
asyncio.run(main())

Related

Python-Websockets: sends messages but cannot receive messages asynchronously

I'm trying to create a full duplex client that sends and receives asynchronously at the same time, using python's websockets package.
The server simply receives a message and echoes it back.
when the client sends all the messages, but doesn't receive anything at all, as if either the send is blocking the receive handler, or the handler is stuck and never updates the data.
However, the server ensures that it both received and sent the data, so I doubt that it's the problem.
I'm genuinely new to async, multithreading, and network programming in general, but this code will be reflected on an applicated that buffers audios from an incoming systems, and sends it to another service, also it can receive any messages from that service at any time regarding this session.
python 3.9.15
websockets==10.4
I've followed the tutorial on the official websockets documentation:
https://websockets.readthedocs.io/en/stable/howto/patterns.html#consumer-and-producer
Client Code:
`
import asyncio
import websockets
sent = []
received = []
URL = "ws://localhost:8001"
async def update_sent(message):
with open("sent.txt", "a+") as f:
print(message, file=f)
sent.append(message)
return 0
async def update_received(message):
with open("recv.txt", "a+") as f:
print(message, file=f)
received.append(message)
return 0
async def sending_handler(websocket):
while True:
try:
message = input("send message:>")
await websocket.send(message)
await update_sent(message)
except Exception as e:
print("Sender: connection closed due to Exception", e)
break
async def receive_handler(websocket):
while True:
try:
message = await websocket.recv()
await update_received(message)
except Exception as e:
print("Receiver: connection closed due to Exception", e)
break
async def full_duplex_handler(websocket):
receiving_task = asyncio.create_task(receive_handler(websocket))
sending_task = asyncio.create_task(sending_handler(websocket))
done, pending = await asyncio.wait([receiving_task, sending_task],
return_when=asyncio.FIRST_COMPLETED)
# return_when=asyncio.FIRST_EXCEPTION)
for task in pending:
print(task)
task.cancel()
async def gather_handler(websocket):
await asyncio.gather(
sending_handler(websocket),
receive_handler(websocket),
)
# using asyncio.wait
async def main_1(url=URL):
async with websockets.connect(url) as websocket:
await full_duplex_handler(websocket)
# using asyncio.gather
# async def main_2(url=URL):
# async with websockets.connect(url) as websocket:
# await gather_handler(websocket)
if __name__ == "__main__":
asyncio.run(main_1())
# asyncio.run(main_2())
`
Server code:
`
import asyncio
import websockets
msgs = []
sent = []
async def handle_send(websocket, message):
await websocket.send(message)
msgs.append(message)
async def handle_recv(websocket):
message = await websocket.recv()
sent.append(message)
return f"echo {message}"
async def handler(websocket):
while True:
try:
message = await handle_recv(websocket)
await handle_send(websocket, message)
except Exception as e:
print(e)
print(msgs)
print(sent)
break
async def main():
async with websockets.serve(handler, "localhost", 8001):
await asyncio.Future()
if __name__ == "__main__":
print("starting the server now")
asyncio.run(main())
`
After sending some messages, all sent and received messages should be written to a file,
but only sent messages are received and processed.
TL;DR
I've put a sleep statement:
await asyncio.sleep(0.02)
in the sending_handler while loop, and it resolved the problem,
apparently the issue was that the sender is way faster than the receiver, that it keeps locking the resources for its use, while the receiver is being blocked.
Any shorter sleep durations can't solve this problem.
final while loop:
async def sending_handler(websocket):
while True:
await asyncio.sleep(0.02)
try:
message = input("send message:>")
await websocket.send(message)
await update_sent(message)
except Exception as e:
print("Sender: connection closed due to Exception", e)
break
Hope this answer helps anyone else who faces the same problem

I need help to connection websocket in python3

I try connect to websocket but i have error:
server rejected WebSocket connection: HTTP 403
but when i try in website it's run like this website:
http://www.epetool.com/websocket/
the code in python 3.9:
import asyncio
import websockets
async def handler(websocket):
while True:
message = await websocket.recv()
print(message)
async def main():
url = "wss://io.dexscreener.com/dex/screener/pairs/h6/1?rankBy[key]=volume&rankBy[order]=desc&filters[buys][h1][min]=100&filters[sells][h1][min]=100&filters[liquidity][min]=500&filters[pairAge][max]=1&filters[chainIds][0]=bsc"
async with websockets.connect(url) as ws:
await handler(ws)
await asyncio.Future() # run forever
if __name__ == "__main__":
asyncio.run(main())

Python websockets fast one way but 10x slower with response

I have a sanic webserver running websockets, behind the scenes its using the "websockets" library.
Server
from asyncio import sleep
from sanic import Sanic
app = Sanic("websocket test")
#app.websocket("/")
async def test(_, ws):
while True:
data = await ws.recv()
data = await ws.send('Hi')
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8000)
Client
import asyncio
import websockets
async def hello():
uri = "ws://localhost:8000"
async with websockets.connect(uri) as websocket:
while iteration:
await websocket.send("Hi")
await websocket.recv()
asyncio.get_event_loop().run_until_complete(hello())
When I remove ws.send('Hi') from the server and await websocket.recv() from the client i can get 58000 messages a second, once I start listening for a response it goes all the way down to 6000 messages a second, I am just curious what is making this run 10x slower when the server responds.
I think the solution here would be to seperate your send and recv into seperate tasks so they can yield concurrently.
async def producer(send):
while True:
await send("...")
await asyncio.sleep(1)
async def consumer(recv):
while True:
message = await recv
print(message)
async def test(request, ws):
request.app.add_task(producer(ws.send)))
request.app.add_task(consumer(ws.recv))
Obviously, this is a very simple example, and at the very least you should use some sort of a Queue for your producer.
But, when you break them into seperate tasks, then your recv is not blocked by send.

Python websockets client keep connection open

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.

Socket Listen for Events While Running Heartbeat

I am trying to setup a listener for socket events while concurrently sending a ping message in the background.
The message received events fire, but only after the next ping completes. There seems to be something blocking.
Here's a mockup of the code I'm running.
import asyncio
import websockets
message = "First Message"
ping = "ping"
url = 'www.socketurl.com'
async def consumer_handler(websocket):
message = await websocket.recv()
if message:
print(message)
async def pingFunc(socket):
await asyncio.sleep(4)
await socket.send(ping)
async def connect(uri):
async with websockets.connect(uri,timeout=5) as websocket:
await websocket.send(message) #send hello message that returns a few pieces of info
while True:
await consumer_handler(websocket) #wait for messages and handle them
await pingFunc(websocket) #keep the connection alive
loop = asyncio.get_event_loop()
loop.run_until_complete(
connect(url)
)

Categories