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
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
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)
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)
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!!!!")
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