Python AsyncIO stream reader not reading properly - python

I have created server which reads message and the client will write the message(which contain header
message concat with body) .However my server not reading properly, At first time reading entire message and the second time it just read the header not reading the Body.
In server.py
import asyncio
from _socket import AF_INET
async def handle_echo(reader, writer):
data = await reader.read(1024)
print(data)
async def main():
server = await asyncio.start_server(
handle_echo, '127.0.0.1', 449, family=AF_INET)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
asyncio.run(main())
In client snippet:
message = loadImage()
writer.write(len(json.dumps(message)).to_bytes(4, 'little') + json.dumps(message).encode("utf-8"))
await writer.drain()
And also checked in Wire shark same message for both client request.
both header and body length are same.
Could please help to resolve?

Related

Python: handle multiple websocket connections

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)?

Ensure "seamless" connection when websocket connections drop in python

I am writing a script to receive fmp4 data from a WebSocket. After a few seconds, the connection seemingly drops and if I reconnect, there is a noticeable lag-spike in the video file.
here's my driver code:
import websockets
import asyncio
async def listen():
url = URL
while True:
async with websockets.connect(url) as ws:
try:
while True:
msg = await ws.recv()
print(msg)
except Exception:
print(f"Connection Dropped, reconnecting...")
await ws.close()
asyncio.run(listen())
I've tried switching up the ping interval, removing the keepalive ping, and reducing the read size, but all to no avail. How is this sort of problem mitigated?

Testing asynchronous sockets in python

I am studying asynchronous sockets in python these days for a bigger project. I just used the asyncio module and I referred the streams official documentation. For test purposes I created a server and a client that the server can handle a single client connected and after client is connected both server and client can chat each other.
server.py
import asyncio
async def handle(reader, writer):
while True:
data = await reader.read(100)
message_recieved = data.decode()
addr = writer.get_extra_info('peername')
print(f'{addr}::::{message_recieved}')
message_toSend = input('>>')
writer.write(message_toSend.encode())
await writer.drain()
async def main():
server = await asyncio.start_server(handle, '127.0.0.1', 10001)
addr = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
asyncio.run(main())
client.py
import asyncio
async def client():
reader, writer = await asyncio.open_connection('127.0.0.1', 10001)
while True:
message = input('>>')
writer.write(message.encode())
data = await reader.read(100)
print(f'Recieved: {data.decode()}')
asyncio.run(client())
This is working fine. But now I have few questions.
How can I check whether is it working asynchronously?
Is it ok to use while loops like I did? (the reason for this question is I feel like when I used a while loop the loop becomes a synchronous part)?
Is this the correct way to code a simple client and server or are there any better ways of doing it?
I highly appreciate if someone experienced with this can help me.

Streaming list of dictionaries in Python with asyncio

I am working on Python server/client application where the server receives some data from the client and based on this data it collects a list of dictionaries from an embedded k/v store and streams it back.
I put here a code that reproduces the error. There is a reason why I put everything into separate functions on server side (clients send different requests).
The problem is that the server sends faster than the client can consume and the client reads several responses at a time, sometimes it is just a part of the message which has been truncated. I thought writelines/readline pair will read from the socket appropriately, but I think I missed something. write/drain also overloads the socket and once multiple results are read the client failes because chunked serialized dictionary is read to orjson.loads.
What is the proper way to solve this problem? Thank you in advance!
Server:
import orjson
async def getResult(cnt : int):
await asyncio.sleep(0)
result = []
for i in range(cnt):
result.append({"key" : i})
return result
async def send(writer, list_of_dict):
for r in list_of_dict:
print(f"\nSending: {r}")
writer.writelines([orjson.dumps(r)])
await writer.drain()
# sending END signal
writer.writelines([orjson.dumps("END")])
await writer.drain()
async def handleClient(reader, writer):
addr = writer.get_extra_info('peername')
print(f"Connection from {addr}")
data = await reader.readline()
message = orjson.loads(data)
print(f"Received {message} from {addr}")
counter = message["send_me"]
responses = await getResult(counter)
await send(writer, responses)
print("Close the client socket")
writer.close()
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handleClient, '127.0.0.1', 4000, loop=loop)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
Client
import asyncio
import orjson
async def async_client(loop):
reader, writer = await asyncio.open_connection('127.0.0.1', 4000, loop=loop)
counter = 5
print(f"Request counter: {counter}")
# in real life the message is a complex dictionary
msg = {"send_me" : counter}
writer.writelines([orjson.dumps(msg)])
#without write_eof the server reader.readline() waits for data and blocks
if writer.can_write_eof():
writer.write_eof()
while True:
data = await reader.readline()
if data:
print(data)
r = orjson.loads(data)
print(f"Received: {r}")
if r == "END":
print("server completed")
break
else:
await asyncio.sleep(0.1)
print('Close the socket')
writer.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(async_client(loop))
loop.close()
Error:
>python echo_client.py
Request counter: 5
b'{"key":0}{"key":1}{"key":2}{"key":3}{"key":4}"END"'
Traceback (most recent call last):
File "echo_client.py", line 32, in <module>
loop.run_until_complete(async_client(loop))
File "C:\Program Files (x86)\Anaconda\lib\asyncio\base_events.py", line 587, in run_until_complete
return future.result()
File "echo_client.py", line 21, in async_client
r = orjson.loads(data)
orjson.JSONDecodeError: trailing characters at line 1 column 10: line 1 column 1 (char 0)
I think the problem is much simpler: writelines doesn't do what you think it does. It doesn't insert newline characters, it just writes any data you give it. This is why the readline() by your client picks up the payload and "END" concatenated together. This is also why you need write_eof in the other direction.
If you want to write a line, then just write a newline character (byte) after your payload. You can abstract that in a function that handles it for you:
async def write_msg(writer, msg):
writer.write(orjson.dumps(msg))
writer.write('\n')
await writer.drain()
async def read_msg(reader):
line = await reader.readline()
return orjson.loads(line)
You can use these on both the client and the server to communicate.
On an aside note, you should probably switch to the newer asyncio.run() API which creates and correctly tears down the event loop with a single async entry point. Your server setup would look like this:
async def main():
await asyncio.start_server(handleClient, '127.0.0.1', 4000)
await server.wait_closed()
asyncio.run(main())

asyncio server client terminates early without output

I'm learning to play around with the asyncio library in py3.5 syntax with async def and await, and trying to write a simple server/client architecture.
For some reason, the client never receives the message and terminates early:
Client
IP = ''
PORT = 8880
import asyncio
import multiprocessing
import ssl
async def start_client(loop):
reader, writer = await asyncio.open_connection(IP, PORT, loop=loop)
writer.write("Gimme gimme gimme".encode())
writer.close()
data = await reader.read()
print(data.decode())
loop = asyncio.get_event_loop()
loop.run_until_complete(start_client(loop))
loop.close()
Server
IP = ''
PORT = 8880
import asyncio
import requests
import json
async def handle_echo(reader, writer):
data = await reader.read()
response = await whatsup()
print(response)
writer.write(response.encode())
writer.write_eof()
await writer.drain()
writer.close()
async def whatsup():
return "Hello there!"
loop = asyncio.get_event_loop()
server = asyncio.start_server(handle_echo,
IP,
PORT,
loop=loop)
server = loop.run_until_complete(server)
try:
loop.run_forever()
except:
pass
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
What I observe is that the server was able to print out the "Hello world!", and return successfully, but from what I can gather, the client reads a total of 0 bytes and just exits.
What I tried already
I tried doing:
while not reader.at_eof():
data = await reader.read(100)
print(data.decode())
But it evaluates at_eof() to be true and exits early as well.
OK I found the solution:
read() will read until eof marker. We need to do writer.write_eof() on both sides for the read() to be read.
Here's the solution code:
async def handle_echo(reader, writer):
data = await reader.read()
response = await whatsup()
print(response)
writer.write(response.encode())
writer.write_eof()
await writer.drain()
writer.close()
async def start_client(loop):
reader, writer = await asyncio.open_connection(IP, PORT, loop=loop)
writer.write("Gimme gimme gimme".encode())
writer.write_eof() # crucial here
writer.close()
data = await reader.read()
print(data.decode())

Categories