125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
import asyncio
|
|
import collections
|
|
import logging
|
|
import signal
|
|
import sys
|
|
|
|
|
|
class Client:
|
|
def __init__(self, host='localhost', port=3001,
|
|
buffer_chunk_size=10**4, buffer_length_limit=10**4):
|
|
self._host = host
|
|
self._port = port
|
|
self._stopping = False
|
|
self.buffer = collections.deque() # Shared queue of bytes
|
|
self._buffer_chunk_size = buffer_chunk_size # How many bytes per chunk
|
|
self._buffer_length_limit = buffer_length_limit # How many chunks in buffer
|
|
self._file_path = None
|
|
self._working = False
|
|
|
|
@property
|
|
def host(self) -> str:
|
|
return self._host
|
|
|
|
@property
|
|
def port(self) -> int:
|
|
return self._port
|
|
|
|
@property
|
|
def stopping(self) -> bool:
|
|
return self._stopping
|
|
|
|
@property
|
|
def buffer_length_limit(self) -> int:
|
|
return self._buffer_length_limit
|
|
|
|
@property
|
|
def buffer_chunk_size(self) -> int:
|
|
return self._buffer_chunk_size
|
|
|
|
@property
|
|
def file_path(self) -> str:
|
|
return self._file_path
|
|
|
|
@property
|
|
def working(self) -> bool:
|
|
return self._working
|
|
|
|
async def run_sending_client(self, file_path='~/output.txt'):
|
|
self._file_path = file_path
|
|
_, writer = await asyncio.open_connection(host=self.host, port=self.port)
|
|
await self.send(writer=writer)
|
|
|
|
async def send(self, writer: asyncio.StreamWriter):
|
|
self._working = True
|
|
with open(self.file_path, 'rb') as file_to_send:
|
|
while not self.stopping:
|
|
output_data = file_to_send.read(self.buffer_chunk_size)
|
|
writer.write(output_data)
|
|
await writer.drain()
|
|
|
|
async def run_receiving_client(self, file_path='~/input.txt'):
|
|
self._file_path = file_path
|
|
reader, _ = await asyncio.open_connection(host=self.host, port=self.port)
|
|
await self.receive(reader=reader)
|
|
|
|
async def receive(self, reader: asyncio.StreamReader):
|
|
self._working = True
|
|
with open(self.file_path, 'wb') as file_to_receive:
|
|
while not self.stopping:
|
|
try:
|
|
input_data = await reader.readexactly(self.buffer_chunk_size)
|
|
except asyncio.IncompleteReadError as e:
|
|
input_data = e.partial
|
|
file_to_receive.write(input_data)
|
|
|
|
def stop(self, *_):
|
|
if self.working:
|
|
logging.info("Received interruption signal, stopping...")
|
|
self._stopping = True
|
|
else:
|
|
raise KeyboardInterrupt("Not working yet...")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
log_formatter = logging.Formatter(
|
|
"%(asctime)s [%(module)-15s %(levelname)-8s] %(message)s",
|
|
style='%'
|
|
)
|
|
root_logger = logging.getLogger()
|
|
root_logger.setLevel(logging.DEBUG)
|
|
|
|
console_handler = logging.StreamHandler()
|
|
console_handler.setFormatter(log_formatter)
|
|
console_handler.setLevel(logging.DEBUG)
|
|
root_logger.addHandler(console_handler)
|
|
|
|
if len(sys.argv) > 1:
|
|
action = sys.argv[1]
|
|
else:
|
|
action = input("Do you want to (R)eceive or (S)end a file?\t\t")
|
|
|
|
action = (
|
|
'send'
|
|
if action.lower() == 's'
|
|
else 'receive'
|
|
)
|
|
if len(sys.argv) > 2:
|
|
file_path = sys.argv[2]
|
|
else:
|
|
file_path = input(f"Enter file to {action}:\t\t\t\t\t\t")
|
|
|
|
loop = asyncio.get_event_loop()
|
|
client = Client(
|
|
host='127.0.0.1',
|
|
port=5000,
|
|
)
|
|
loop.add_signal_handler(signal.SIGINT, client.stop, loop)
|
|
logging.info("Starting client...")
|
|
if action.lower() == 'send':
|
|
loop.run_until_complete(client.run_sending_client(file_path=file_path))
|
|
else:
|
|
loop.run_until_complete(client.run_receiving_client(file_path=file_path))
|
|
loop.close()
|
|
logging.info("Stopped server")
|