diff options
| author | Andrew <saintruler@gmail.com> | 2019-04-22 19:02:27 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2019-04-22 19:02:27 +0400 |
| commit | fae0c2ca1055e2c94299d16b12a20e84fbae1845 (patch) | |
| tree | 991ebbb7a72b1d683735cf9ff3f9e33cdb203dc6 /day4/task4 | |
| parent | 402d0d2b9ebd76b8e99eddceeb85c4a66030b51b (diff) | |
WIP: Четвертый день, четвертая задача
Diffstat (limited to 'day4/task4')
| -rw-r--r-- | day4/task4/client.py | 131 | ||||
| -rw-r--r-- | day4/task4/server.py | 110 |
2 files changed, 241 insertions, 0 deletions
diff --git a/day4/task4/client.py b/day4/task4/client.py new file mode 100644 index 0000000..33ea084 --- /dev/null +++ b/day4/task4/client.py @@ -0,0 +1,131 @@ +import socket +from time import sleep +import struct +import json +import shelve + + +def pack_data(data): + encoded = data.encode() + return struct.pack('I', len(encoded)) + encoded + + +def prepare_data(data): + cmd, args = data.split(' ', maxsplit=1) + + if cmd == 'get': + return json.dumps({'method': 'get', 'path': args}) + + elif cmd == 'cp': + paths = args.split() + if len(paths) != 2: + raise ValueError('Неверное количество путей в параметрах.') + src, dest = args.split() + if src not in db: + return json.dumps({'method': 'get_cp', 'path': src}) + with open(dest, 'wb') as f: + f.write(db[src]) + + else: + raise ValueError('Команда не распознана') + + +def parse_response(data): + return json.loads(data.decode()) + + +def get_response(connection): + buffer = b'' + while len(buffer) < DATATYPE_SIZE: + data = connection.recv(DATATYPE_SIZE - len(buffer)) + if not data: + raise ConnectionError('Connection was closed') + + buffer += data + + size_packed, buffer = buffer[:DATATYPE_SIZE], buffer[DATATYPE_SIZE:] + if not size_packed: + raise ConnectionError('Connection was closed') + + (size,) = struct.unpack('I', size_packed) + data = connection.recv(size - len(buffer)) + return parse_response(buffer + data) + + +def download_file(filesize, connection): + data = b'' + count = 0 + while count < filesize: + (size,) = struct.unpack('I', connection.recv(DATATYPE_SIZE)) + data += connection.recv(size) + count += size + + return data + + +def handle_response(data, connection): + if data['status'] == 0: + print(f'Загружаем файл размером {data["size"]} байт...') + file_data = download_file(data['size'], connection) + db[data['path']] = file_data + print('Загрузка завершена') + + elif data['status'] == 1: + print(data['reason']) + + elif data['status'] == 2: + print('Неизвестная ошибка:') + print(data['reason']) + + +def try_connect(): + cnt = 0 + sock = socket.socket() + while cnt < 10: + try: + cnt += 1 + sock.connect(ADDR) + + except ConnectionRefusedError: + print('Не удалось подключиться к серверу. Следующая попытка через 3 секунды...') + sleep(3) + + else: + return sock + + else: + print('Не удалось подключиться к серверу через 10 попыток.\nОстанавливаемся...') + sock.close() + return + + +HOST, PORT = ADDR = 'localhost', 6000 +DATATYPE_SIZE = 4 # int + +sock = try_connect() +if sock is None: + quit() + + +db = shelve.open('backdoor.db') + +while True: + try: + data = input('Введите команду:\n') + data = prepare_data(data) + if data is None: + continue + + except KeyboardInterrupt: + break + + except ValueError as err: + print(err) + + else: + packet = pack_data(data) + sock.sendall(packet) + response = get_response(sock) + handle_response(response, sock) + +sock.close() diff --git a/day4/task4/server.py b/day4/task4/server.py new file mode 100644 index 0000000..980eebc --- /dev/null +++ b/day4/task4/server.py @@ -0,0 +1,110 @@ +import socket +import struct +import json + + +def pack_str(data: str): + encoded = data.encode() + return struct.pack('I', len(encoded)) + encoded + + +def pack_bytes(data: bytes): + return struct.pack('I', len(data)) + data + + +def handle_request(data, connection): + request = json.loads(data) + + # Хотя метод только один, но все равно лучше + # сразу написать расширяемое приложение + if request['method'] == 'get': + path = request['path'] + + try: + with open(path, 'rb') as f: + file_data = f.read() + + except FileNotFoundError: + header = json.dumps({'status': 1, 'reason': 'Файл не найден'}) + connection.sendall(pack_str(header)) + + except PermissionError: + header = json.dumps({'status': 1, 'reason': 'Файл не доступен для чтения'}) + connection.sendall(pack_str(header)) + + # Я знаю, что это плохо, но сервер не должен падать, + # а других ошибок я не смог вспомнить + except Exception as e: + header = json.dumps({'status': 2, 'reason': str(e)}) + connection.sendall(pack_str(header)) + + else: + length = len(file_data) + header = json.dumps({'status': 0, 'size': length, 'path': request['path']}) + connection.sendall(pack_str(header)) + i = 0 + while i < length: + packet = pack_bytes(file_data[i: i + CHUNK_SIZE]) + connection.sendall(packet) + i += CHUNK_SIZE + + +def get_data(connection): + buffer = b'' + while len(buffer) < 4: + data = connection.recv(4 - len(buffer)) + if not data: + raise ConnectionError('Connection was closed') + + buffer += data + + size_packed, buffer = buffer[:4], buffer[4:] + if not size_packed: + raise ConnectionError('Connection was closed') + + (size,) = struct.unpack('I', size_packed) + data = connection.recv(size - len(buffer)) + return buffer + data + + +def handle_connection(connection, address): + while True: + try: + data = get_data(connection) + + except ConnectionResetError: + print(f'Connection with {address[0]} was unexpectedly closed') + break + + except ConnectionError: + print(f'Connection with {address[0]} was ended') + break + + except KeyboardInterrupt: + raise KeyboardInterrupt() + + else: + if not data: + print(f'Connection with {address[0]} was ended') + break + handle_request(data.decode(), connection) + + +HOST, PORT = ADDR = '0.0.0.0', 6000 +CHUNK_SIZE = 4096 +sock = socket.socket() +sock.bind(ADDR) +sock.listen(5) + +while True: + print('Listening for new connections...') + try: + connection, address = sock.accept() + print(f'Got a connection from {address[0]}') + handle_connection(connection, address) + + except KeyboardInterrupt: + print('Stopping server...') + sock.close() + print('Server stopped') + break |