ConnectionResetError exception when receiving EOF in AsyncIO framework - python

I have a very simple chatroom (chat_server.py and chat_client.py) written asynchronously using AsyncIO framework. The server simply receives messages and tell the client that I received your message, that's it. The code is from Łukasz Langa's tutorial.
chat_server.py
from typing import *
import asyncio
async def handle_connection(
reader: asyncio.StreamReader,
writer: asyncio.StreamWriter,
) -> None:
addr = writer.get_extra_info("peername")
while message := await reader.read(100):
text = message.decode()
print(f"Received {text!r} from {addr!r}")
print(f"Sending {text!r}")
writer.write(message)
await writer.drain()
if text == "quit\n":
break
print("Closing the connection")
writer.close()
async def main() -> None:
server = await asyncio.start_server(handle_connection, "127.0.0.1", 8888)
addr = server.sockets[0].getsockname() if server.sockets else "unknown"
print(f"Serving on {addr}")
async with server:
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main())
chat_client1.py
from typing import *
import asyncio
import sys
async def send_fine(file: IO[str]) -> None:
reader, writer = await asyncio.open_connection("127.0.0.1", 8888)
for message in file:
writer.write(message.encode()) ###############
await writer.drain() ###############
data = await reader.read(100)
text = data.decode()
print(f"Received {text!r}")
if text == "quit\n":
break
print("Closing the connection")
writer.close()
if __name__ == "__main__":
asyncio.run(send_fine(sys.stdin))
Having the above code, I can cause EOF by ctrl+D and the chat_server.py will handle that correctly by closing the connection without any exception.
However, instead of sending line by line, if I send byte by byte, it raises a ConnectionResetError when I press ctrl+D.
I've put ####... to show which lines where changed:
chat_cleint2.py
from typing import *
import asyncio
import sys
async def send_fine(file: IO[str]) -> None:
reader, writer = await asyncio.open_connection("127.0.0.1", 8888)
for message in file:
for ch in message.encode(): ###############
await asyncio.sleep(0.1) ###############
writer.write(bytes([ch])) ###############
await writer.drain()
data = await reader.read(100)
text = data.decode()
print(f"Received {text!r}")
if text == "quit\n":
break
print("Closing the connection")
writer.close()
if __name__ == "__main__":
asyncio.run(send_fine(sys.stdin))
I'm mostly interested in why there is no ConnectionResetError in chat_client1.py but there is in chat_client2.py.

Related

joining hundreds of irc channels socket dying (twitch)

I'm trying to join hundreds of channels with my chatbot on twitch. However the socket gets connection was forcibly closed by the remote host error, my problem is I don't particularly know how to add multiple sockets so if one dies then I can rejoin the channels on that socket and continue, instead it closes the client and tries to join all of the channels again.
Here is my code:
import socket
import time
import errno
with open('channels.txt') as f:
channel_list = f.readlines()
def connect():
global sock
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect(('irc.chat.twitch.tv', 6667))
def sendRaw(data, log:bool=True):
try:
if log:
print(f"sending raw: {data}")
sock.send(bytes(data + '\r\n', 'utf-8'))
except IOError as e:
if e.errno == errno.EPIPE:
pass
def joinchannel(channel):
channel = "#" + channel.lower() if not channel.startswith("#") else channel.lower()
sendRaw('JOIN ' + channel)
def login_and_join():
sendRaw('PASS oauth:') # less likely to leak
sendRaw('NICK justinfan0987')
# requests more information
sendRaw('CAP REQ :twitch.tv/commands')
sendRaw('CAP REQ :twitch.tv/tags')
sendRaw('JOIN #turtoise')
sendRaw('JOIN #nimbot0')
sendRaw('JOIN #peeandpoob')
for channel in channel_list:
joinchannel(channel)
def loop():
while True:
time.sleep(0.1)
try:
data = sock.recv(4096)
decoded_data = data.decode("utf-8").split("\r\n")
if decoded_data is None:
print("no data")
print(decoded_data)
except Exception as e:
print(e)
def run_channel_joiner():
connect()
login_and_join()
loop()
run_channel_joiner()
And here are some channels to test with:
https://pastebin.com/avn0PVVg

Running multiple sockets using asyncio in python

Setup: Python 3.7.4
I am trying to create 6 sockets using asyncio listening on different ports. I tried to implement it like this.
Code:
import asyncio
async def client_thread(reader, writer):
while True:
package_type = await reader.read(100)
if not package_type:
break
if(package_type[0] == 1) :
nn_output = get_some_bytes1
elif (package_type[0] == 2) :
nn_output = get_some_bytes2
elif (package_type[0] == 3) :
nn_output = get_some_bytes3
elif (package_type[0] == 4) :
nn_output = get_some_bytes4
elif (package_type[0] == 5) :
nn_output = get_some_bytes5
else:
nn_output = get_bytes
writer.write(nn_output)
await writer.drain()
async def start_servers(host, port):
server = await asyncio.start_server(client_thread, host, port)
await server.serve_forever()
def enable_sockets():
try:
host = '127.0.0.1'
port = 60000
sockets_number = 6
for i in range(sockets_number):
asyncio.run(start_servers(host,port+i))
except:
print("Exception")
enable_sockets()
So when i receive a call from a client on port 60000 depending on the type of request to be able to serve the client while i serve another client on port 60001.
Each client will send values from 1 to 5. Client 1 -> 1 Client 2 -> 2 etc.
The code is failing at this moment at package_type = await reader.read(100) when i try to start the server
Rewrite your enable_sockets function like this:
def enable_sockets():
try:
host = '127.0.0.1'
port = 60000
sockets_number = 6
loop = asyncio.get_event_loop()
for i in range(sockets_number):
loop.create_task(start_servers(host,port+i))
loop.run_forever()
except Exception as exc:
print(exc)

Use websocket and I/O serial together in python?

I am struggling to have my both my websocket script and my I/O serial script running together in one together.
Just some basic info before I continue:
I am using Windows PC(Have no access to linux PC)
This is the reason why I am using the AIOserial library instead of pyserial-asyncio
I have no "super" experience with asyncio, so be kind please :)
Here is my "old" websocket script:
from aiohttp import web
import socketio
import aiohttp_cors
import asyncio
import random
# creates a new Async Socket IO Server
sio = socketio.AsyncServer()
# Creates
app = web.Application()
sio.attach(app)
server_is_responding = "Message from the server:"
the_response = "Hello there!"
async def index(request):
with open('index.html') as f:
print("Somebody entered the server from the browser!")
return web.Response(text=f.read(), content_type='text/html')
#sio.on("android-device")
async def message(sid, data):
print("message: ", data)
#return send_message_to_client()
#sio.on('sendTextToServer')
async def message(sid, data):
print("message: " , data)
if data == "hei":
await sio.emit("ServerMessage", {"hehe"})
if data == "lol":
await sio.emit("ServerMessage", {"Message from server:": "hehe, funny right?.."})
else:
await sio.emit("ServerMessage", {"Message from server:": "Hello There!"})
# We bind our aiohttp endpoint to our app
# router
cors = aiohttp_cors.setup(app)
app.router.add_get('/', index)
# We kick off our server
if __name__ == '__main__':
web.run_app(app)
And here is my I/O serial script(which works and read the data), that I am trying to use with some of the websocket functions above:
import asyncio
import websockets
import socketio
import aiohttp_cors
import logging
from AIOExtensions.AIOSerial import (AIOSerial, AIOSerialClosedException,
AIOSerialErrorException, AIOSerialNotOpenException)
logging.basicConfig(level=logging.DEBUG)
sio = socketio.AsyncServer()
async def hello(websocket, path):
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
#sio.on("android-device")
async def message(sid, data):
print("message: ", data)
async def read_IO_serial():
try:
async with AIOSerial('COM8', baudrate=115200, line_mode=True) as aios:
await asyncio.sleep(100)
try:
while True:
# read with timeout
rcvd = await asyncio.wait_for(aios.read(), timeout=1.0)
# print the data received
print(f"data received: {rcvd}")
if rcvd == b'RF initialized\n':
print("CATCHED THIS LINE!")
except asyncio.TimeoutError:
print("reception timed out ;-(")
except AIOSerialNotOpenException:
print("Unable to open the port!")
print()
print("Have you specified the right port number? COM7? COM8?")
# port fatal error
except AIOSerialErrorException:
print("Port error!")
# port already closed
except AIOSerialClosedException:
print("Serial port is closed!")
start_server = websockets.serve(hello, "http://192.168.1.6", 8080)
#sio.attach(start_server) # HOW CAN I ATTACH THIS SO IT CAN BE USED WITH THE SIO FUNCTIONS BELOW?
if start_server:
print("Server started!")
asyncio.run(read_IO_serial())
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
As you can see in my first simple websocket script, I could use "sio.attach(app)" which made it possible to listed to events from client, so I need a way of replacing this "app" on my current script..
Someone who can please help me with this?
I solved it using asyncio.gather(), this is how I dit it:
from aiohttp import web
import socketio
import aiohttp_cors
import asyncio
import random
​
import asyncio as aio
import logging
​
import sys
​
# creates a new Async Socket IO Server
sio = socketio.AsyncServer()
# Creates
app = web.Application()
sio.attach(app)
​
server_is_responding = "Message from the server:"
the_response = "Hello there!"
​
​
async def index(request):
with open('index.html') as f:
print("Somebody entered the server from the browser!")
return web.Response(text=f.read(), content_type='text/html')
​
​
#sio.event
async def join(sid, message):
sio.enter_room(sid, message['room'])
await sio.emit('my_response', {'data': 'Entered room: ' + message['room']}, room=sid)
​
​
#sio.on("android-device")
async def message(sid, data):
print("message: ", data)
​
​
#sio.on("receiveMessageFromServer")
async def message(sid, data):
print("message: ", data)
# await asyncio.sleep(1 * random.random())
return "OKKKK", 123
​
​
​
from AIOExtensions.AIOSerial import (AIOSerial, AIOSerialClosedException,
AIOSerialErrorException, AIOSerialNotOpenException)
​
logging.basicConfig(level=logging.DEBUG)
​
​
async def read_IO_serial():
try:
​
async with AIOSerial('COM8', baudrate=115200, line_mode=True) as aios:
# aios.sp.baudrate = 230400
# aios.sp.baudrate = 115200
​
# await aios.write(b"AT\r\n")
​
# await aios.read()
​
# await aios.close()
​
await aio.sleep(100)
​
try:
while True:
​
# read with timeout
rcvd = await aio.wait_for(aios.read(), timeout=1.0)
# print the data received
print(f"data received: {rcvd}")
​
if rcvd == b'RF initialized\n':
print("CATCHED THIS LINE!")
​
​
except aio.TimeoutError:
print("reception timed out ;-(")
​
except AIOSerialNotOpenException:
print("Unable to open the port!")
print()
print("Have you specified the right port number? COM7? COM8?")
# port fatal error
except AIOSerialErrorException:
print("Port error!")
# port already closed
except AIOSerialClosedException:
print("Serial port is closed!")
​
​
async def on_startup(app):
pass
​
​
cors = aiohttp_cors.setup(app)
app.router.add_get('/', index)
​
# We kick off our server
if __name__ == '__main__':
loop = asyncio.get_event_loop()
group2 = asyncio.gather(read_IO_serial())
group1 = asyncio.gather(web.run_app(app))
​
all_groups = asyncio.gather(group1, group2)
​
results = loop.run_until_complete(all_groups)
​
# loop.close()
​
#print(results)

How to send a message to Server with Websocket

I'm trying to send messages to a Server to get answers.
I've tried to use official websocket APIs from the site but I don't understand them or can't make them work as I wish, so I'm trying to build it.
import asyncio
import websockets
async def test():
async with websockets.connect('wss://www.bitmex.com/realtime') as websocket:
await websocket.send("ping")
#OR await websocket.send({"op": "subscribe", "args": [<SubscriptionTopic>]})
response = await websocket.recv()
print(response)
asyncio.get_event_loop().run_until_complete(test())
I recieve that I'm connected but I don't recieve "pong" as an answer to "ping", nor "Good you are subscribed to this Topic" as I recieve when trying the commands on a echo website.
#!/usr/bin/env python3
import asyncio
import websockets
import json
var = []
async def test():
async with websockets.connect('wss://www.bitmex.com/realtime') as websocket:
response = await websocket.recv()
print(response)
await websocket.send(json.dumps({"op": "subscribe", "args": "trade:TRXH19"}))
response = await websocket.recv()
resp = await websocket.recv()
print(json.loads(resp))
sum=0
while True:
resp = await websocket.recv()
jj = json.loads(resp)["data"][0]
var.append(jj)
size = jj["size"]
side = jj["side"]
coin = jj["symbol"]
if side=="Buy":
sum+=size
else:
sum-=size
print(coin)
print(size)
print(side)
print("Totale = ", sum )
while True:
asyncio.get_event_loop().run_until_complete(test())
print(var)
print("Ciclo Finito!!!!")
That's because you have to read received data after every send.
#!/usr/bin/env python3
import asyncio
import websockets
import json
var = []
async def test():
async with websockets.connect('wss://www.bitmex.com/realtime') as websocket:
response = await websocket.recv()
print(response)
await websocket.send("ping")
response = await websocket.recv()
print(response)
var.append(response)
await websocket.send(json.dumps({"op": "subscribe", "args": "test"}))
response = await websocket.recv()
print(response)
asyncio.get_event_loop().run_until_complete(test())
print(var)
Output:
{"info":"Welcome to the BitMEX Realtime API.","version":"2019-02-12T19:21:05.000Z","timestamp":"2019-02-17T14:38:32.696Z","docs":"https://www.bitmex.com/app/wsAPI","limit":{"remaining":37}}
pong
{"status":400,"error":"Unknown table: test","meta":{},"request":{"op":"subscribe","args":"test"}}
['pong']
Edit - code with handling websockets fall and multiple data:
#!/usr/bin/env python3
import asyncio
import websockets
import json
total = 0
async def test():
async with websockets.connect('wss://www.bitmex.com/realtime') as websocket:
response = await websocket.recv()
print(response)
await websocket.send(json.dumps({"op": "subscribe", "args": "trade:TRXH19"}))
response = await websocket.recv()
#resp = await websocket.recv()
#print(json.loads(resp))
global total
while True:
resp = await websocket.recv()
print(resp)
for jj in json.loads(resp)["data"]:
size = jj["size"]
side = jj["side"]
coin = jj["symbol"]
if side == "Buy":
total += size
else:
total -= size
print(coin)
print(size)
print(side)
print("Totale = ", total)
while True:
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(test())
except Exception as e:
print(e)
loop.close()
#finally:
print(total)
print("Ciclo Finito!!!!")

Python asyncio / aiohttp error

I am writing a simple producer/consumer app to call multiple URL's asynchronously.
In the following code if I set the conn_count=1, and add 2 items to the Queue it works fine as only one consumer is created. But if I make conn_count=2 and add 4 items to the Queue only 3 request are being made. The other request fails with ClientConnectorError.
Can you please help be debug the reason for failure with multiple consumers? Thank You.
I am using a echo server I created.
Server:
import os
import logging.config
import yaml
from aiohttp import web
import json
def start():
setup_logging()
app = web.Application()
app.router.add_get('/', do_get)
app.router.add_post('/', do_post)
web.run_app(app)
async def do_get(request):
return web.Response(text='hello')
async def do_post(request):
data = await request.json()
return web.Response(text=json.dumps(data))
def setup_logging(
default_path='logging.yaml',
default_level=logging.INFO,
env_key='LOG_CFG'
):
path = default_path
value = os.getenv(env_key, None)
if value:
path = value
if os.path.exists(path):
with open(path, 'rt') as f:
config = yaml.safe_load(f.read())
logging.config.dictConfig(config)
else:
logging.basicConfig(level=default_level)
if __name__ == '__main__':
start()
Client:
import asyncio
import collections
import json
import sys
import async_timeout
from aiohttp import ClientSession, TCPConnector
MAX_CONNECTIONS = 100
URL = 'http://localhost:8080'
InventoryAccount = collections.namedtuple("InventoryAccount", "op_co customer_id")
async def produce(queue, num_consumers):
for i in range(num_consumers * 2):
await queue.put(InventoryAccount(op_co=i, customer_id=i * 100))
for j in range(num_consumers):
await queue.put(None)
async def consumer(n, queue, session, responses):
print('consumer {}: starting'.format(n))
while True:
try:
account = await queue.get()
if account is None:
queue.task_done()
break
else:
print(f"Consumer {n}, Updating cloud prices for account: opCo = {account.op_co!s}, customerId = {account.customer_id!s}")
params = {'opCo': account.op_co, 'customerId': account.customer_id}
headers = {'content-type': 'application/json'}
with async_timeout.timeout(10):
print(f"Consumer {n}, session state " + str(session.closed))
async with session.post(URL,
headers=headers,
data=json.dumps(params)) as response:
assert response.status == 200
responses.append(await response.text())
queue.task_done()
except:
e = sys.exc_info()[0]
print(f"Consumer {n}, Error updating cloud prices for account: opCo = {account.op_co!s}, customerId = {account.customer_id!s}. {e}")
queue.task_done()
print('consumer {}: ending'.format(n))
async def start(loop, session, num_consumers):
queue = asyncio.Queue(maxsize=num_consumers)
responses = []
consumers = [asyncio.ensure_future(loop=loop, coro_or_future=consumer(i, queue, session, responses)) for i in range(num_consumers)]
await produce(queue, num_consumers)
await queue.join()
for consumer_future in consumers:
consumer_future.cancel()
return responses
async def run(loop, conn_count):
async with ClientSession(loop=loop, connector=TCPConnector(verify_ssl=False, limit=conn_count)) as session:
result = await start(loop, session, conn_count)
print("Result: " + str(result))
if __name__ == '__main__':
conn_count = 2
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(run(loop, conn_count))
finally:
loop.close()
Reference:
https://pymotw.com/3/asyncio/synchronization.html
https://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-aiohttp.html
https://hackernoon.com/asyncio-for-the-working-python-developer-5c468e6e2e8e

Categories