From fae0c2ca1055e2c94299d16b12a20e84fbae1845 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 22 Apr 2019 19:02:27 +0400 Subject: =?UTF-8?q?WIP:=20=D0=A7=D0=B5=D1=82=D0=B2=D0=B5=D1=80=D1=82=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=B4=D0=B5=D0=BD=D1=8C,=20=D1=87=D0=B5=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=82=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- day4/task4/client.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++ day4/task4/server.py | 110 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 day4/task4/client.py create mode 100644 day4/task4/server.py 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 -- cgit v1.2.3 From 7e591a381db30bfcd374ebdffdc058f8bbfd24ed Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 22 Apr 2019 21:00:47 +0400 Subject: =?UTF-8?q?=D0=A7=D0=B5=D1=82=D0=B2=D0=B5=D1=80=D1=82=D0=B0=D1=8F?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B0=20=D0=B7=D0=B0=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=88=D0=B5=D0=BD=D0=B0.=20=D0=98=D1=81=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BE=D0=B1=D1=8F=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D0=B0=20tqdm=20(=D1=87=D1=82=D0=BE=D0=B1=D1=8B=20?= =?UTF-8?q?=D0=B1=D1=8B=D0=BB=D0=BE=20=D0=BA=D1=80=D0=B0=D1=81=D0=B8=D0=B2?= =?UTF-8?q?=D0=BE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- day4/task4/client.py | 76 ++++++++++++++++++++++++++++++++-------------------- day4/task4/server.py | 10 +++++-- 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/day4/task4/client.py b/day4/task4/client.py index 33ea084..bc4a40f 100644 --- a/day4/task4/client.py +++ b/day4/task4/client.py @@ -4,40 +4,60 @@ import struct import json import shelve +try: + from tqdm import tqdm + TQDM = True +except ImportError: + print('Для вывода прогресса загрузки файлов установите библиотеку tqdm') + TQDM = False + def pack_data(data): encoded = data.encode() return struct.pack('I', len(encoded)) + encoded -def prepare_data(data): +def handle_input(data): cmd, args = data.split(' ', maxsplit=1) if cmd == 'get': - return json.dumps({'method': 'get', 'path': args}) + make_get_request(args) elif cmd == 'cp': paths = args.split() if len(paths) != 2: raise ValueError('Неверное количество путей в параметрах.') - src, dest = args.split() + + src, dest = paths + + # Если файл не присутствует в БД, то предварительно его загружаем + status = 0 if src not in db: - return json.dumps({'method': 'get_cp', 'path': src}) - with open(dest, 'wb') as f: - f.write(db[src]) + status = make_get_request(src) + + if status == 0: + with open(dest, 'wb') as f: + f.write(db[src]) else: raise ValueError('Команда не распознана') +def make_get_request(path): + packet = pack_data(json.dumps({'method': 'get', 'path': path})) + sock.sendall(packet) + response = get_response() + return handle_response(response) + + def parse_response(data): return json.loads(data.decode()) -def get_response(connection): +def get_response(): buffer = b'' while len(buffer) < DATATYPE_SIZE: - data = connection.recv(DATATYPE_SIZE - len(buffer)) + data = sock.recv(DATATYPE_SIZE - len(buffer)) if not data: raise ConnectionError('Connection was closed') @@ -48,25 +68,31 @@ def get_response(connection): raise ConnectionError('Connection was closed') (size,) = struct.unpack('I', size_packed) - data = connection.recv(size - len(buffer)) + data = sock.recv(size - len(buffer)) return parse_response(buffer + data) -def download_file(filesize, connection): +def download_file(filesize): data = b'' - count = 0 - while count < filesize: - (size,) = struct.unpack('I', connection.recv(DATATYPE_SIZE)) - data += connection.recv(size) - count += size + + if TQDM: + for count in tqdm(range(0, filesize, CHUNK_SIZE)): + (size,) = struct.unpack('I', sock.recv(DATATYPE_SIZE)) + data += sock.recv(size) + else: + count = 0 + while count < filesize: + (size,) = struct.unpack('I', sock.recv(DATATYPE_SIZE)) + data += sock.recv(size) + count += size return data -def handle_response(data, connection): +def handle_response(data): if data['status'] == 0: print(f'Загружаем файл размером {data["size"]} байт...') - file_data = download_file(data['size'], connection) + file_data = download_file(data['size']) db[data['path']] = file_data print('Загрузка завершена') @@ -77,6 +103,8 @@ def handle_response(data, connection): print('Неизвестная ошибка:') print(data['reason']) + return data['status'] + def try_connect(): cnt = 0 @@ -101,6 +129,7 @@ def try_connect(): HOST, PORT = ADDR = 'localhost', 6000 DATATYPE_SIZE = 4 # int +CHUNK_SIZE = 4096000 sock = try_connect() if sock is None: @@ -112,20 +141,9 @@ db = shelve.open('backdoor.db') while True: try: data = input('Введите команду:\n') - data = prepare_data(data) - if data is None: - continue + handle_input(data) 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 index 980eebc..fb88629 100644 --- a/day4/task4/server.py +++ b/day4/task4/server.py @@ -35,18 +35,21 @@ def handle_request(data, connection): # Я знаю, что это плохо, но сервер не должен падать, # а других ошибок я не смог вспомнить except Exception as e: - header = json.dumps({'status': 2, 'reason': str(e)}) + header = json.dumps({'status': 2, 'reason': f'{type(e)}: {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)) + print(f'Started sending file of size {length} bytes...') i = 0 while i < length: packet = pack_bytes(file_data[i: i + CHUNK_SIZE]) connection.sendall(packet) i += CHUNK_SIZE + print(i / length * 100, '%') + print(f'Transmission ended') def get_data(connection): @@ -91,7 +94,7 @@ def handle_connection(connection, address): HOST, PORT = ADDR = '0.0.0.0', 6000 -CHUNK_SIZE = 4096 +CHUNK_SIZE = 4096000 sock = socket.socket() sock.bind(ADDR) sock.listen(5) @@ -103,6 +106,9 @@ while True: print(f'Got a connection from {address[0]}') handle_connection(connection, address) + except ConnectionResetError: + print(f'Connection with {address[0]} was unexpectedly closed') + except KeyboardInterrupt: print('Stopping server...') sock.close() -- cgit v1.2.3 From a0e7cc23b85e9c174fcf4ba8fce40e53ccd43472 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 22 Apr 2019 21:47:08 +0400 Subject: =?UTF-8?q?=D0=9D=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B8?= =?UTF-8?q?=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D0=BB=20=D1=81=D1=82=D1=80=D1=83?= =?UTF-8?q?=D0=BA=D1=82=D1=83=D1=80=D1=83=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=20=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BB=20=D0=B5=D0=B3=D0=BE=20=D1=81=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BC=D0=BE=D1=89=D1=8C=D1=8E=20=D0=9E=D0=9E=D0=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- day4/task4/client_class.py | 132 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 day4/task4/client_class.py diff --git a/day4/task4/client_class.py b/day4/task4/client_class.py new file mode 100644 index 0000000..0925094 --- /dev/null +++ b/day4/task4/client_class.py @@ -0,0 +1,132 @@ +import socket +from time import sleep +import struct +import json +import shelve + +try: + from tqdm import tqdm + TQDM = True +except ImportError: + print('Для вывода прогресса загрузки файлов установите библиотеку tqdm') + TQDM = False + + +DATATYPE_SIZE = 4 # int +CHUNK_SIZE = 4096000 +HOST, PORT = ADDR = 'localhost', 6000 + + +class Client: + def __init__(self, server_host, server_port): + self.server = socket.socket() + self.db = shelve.open('backdoor.db') + + address = server_host, server_port + cnt = 0 + while cnt < 10: + try: + cnt += 1 + self.server.connect(address) + + except ConnectionRefusedError: + print('Не удалось подключиться к серверу. Следующая попытка через 3 секунды...') + sleep(3) + + else: + break + + else: + print('Не удалось подключиться к серверу через 10 попыток.\nОстанавливаемся...') + self.server.close() + + def start(self): + while True: + try: + data = input('Введите команду:\n') + self.handle_input(data) + + except ValueError as err: + print(err) + + except KeyboardInterrupt: + break + + def handle_input(self, data): + cmd, args = data.split(' ', maxsplit=1) + + if cmd == 'get': + self.download_file(args) + + elif cmd == 'cp': + paths = args.split() + if len(paths) != 2: + raise ValueError('Неверное количество путей в параметрах.') + + src, dest = paths + + # Если файл не присутствует в БД, то предварительно его загружаем + if src not in self.db: + self.download_file(src) + + # При любых ошибках при загрузке файла download_file выкинет исключение и исполнение + # не дойдет до этого блока, поэтому можно не проверять наличие src в БД + with open(dest, 'wb') as f: + f.write(self.db[src]) + + else: + raise ValueError('Команда не распознана') + + def download_file(self, path): + response = self.make_get_request(path) + if response['status'] == 0: + print(f'Загружаем файл размером {response["size"]} байт...') + + file_data = self.receive_data(int(response['size']), use_tqdm=True) + self.db[path] = file_data + + print('Загрузка завершена') + + elif response['status'] == 1: + raise ValueError(response['reason']) + + elif response['status'] == 2: + raise ValueError(f'Неизвестная ошибка: {response["reason"]}') + + def make_get_request(self, path): + packet = Client.pack_str(json.dumps({'method': 'get', 'path': path})) + self.server.sendall(packet) + return Client.parse_response(self.receive_data(DATATYPE_SIZE)) + + def receive_data(self, data_size, use_tqdm=False): + data = b'' + + # С копипастой надо бы что-то сделать, но я не знаю что + if not use_tqdm or not TQDM: + while len(data) < data_size: + (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) + data += self.server.recv(packet_size) + if not data: + raise ConnectionError('Connection was closed') + + elif TQDM: + for count in tqdm(range(0, data_size, CHUNK_SIZE)): + (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) + data += self.server.recv(packet_size) + if not data: + raise ConnectionError('Connection was closed') + + return data + + @staticmethod + def pack_str(data): + encoded = data.encode() + return struct.pack('I', len(encoded)) + encoded + + @staticmethod + def parse_response(data): + return json.loads(data.decode()) + + +client = Client(HOST, PORT) +client.start() -- cgit v1.2.3 From 13310196d01eb8e4b04dc03a37cd5b5ef532b8d2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 22 Apr 2019 21:49:47 +0400 Subject: =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=BE=D0=B5?= =?UTF-8?q?=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- day4/task4/client_class.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/day4/task4/client_class.py b/day4/task4/client_class.py index 0925094..854e5e2 100644 --- a/day4/task4/client_class.py +++ b/day4/task4/client_class.py @@ -52,6 +52,8 @@ class Client: except KeyboardInterrupt: break + self.server.close() + def handle_input(self, data): cmd, args = data.split(' ', maxsplit=1) -- cgit v1.2.3 From 7f2e25edde7c4e8bc60514156abf28014a3f0c3c Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 24 Apr 2019 20:48:19 +0400 Subject: =?UTF-8?q?=D0=94=D0=B5=D0=B9=D1=81=D1=82=D0=B2=D1=83=D1=8E=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B5=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5,=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D1=8B=D0=B5?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D1=81=D1=87=D0=B5=D1=82=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B8=D0=BB=20=D0=BA=D0=BB=D0=B8=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=20=D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=B2=20=D0=A1=D0=B8-=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- day4/task4/client.py | 194 +++++++++++++++++++++------------------------ day4/task4/client_class.py | 134 ------------------------------- day4/task4/server.py | 57 +++++++------ 3 files changed, 123 insertions(+), 262 deletions(-) delete mode 100644 day4/task4/client_class.py diff --git a/day4/task4/client.py b/day4/task4/client.py index bc4a40f..b6f0ab8 100644 --- a/day4/task4/client.py +++ b/day4/task4/client.py @@ -12,138 +12,124 @@ except ImportError: TQDM = False -def pack_data(data): - encoded = data.encode() - return struct.pack('I', len(encoded)) + encoded - - -def handle_input(data): - cmd, args = data.split(' ', maxsplit=1) - - if cmd == 'get': - make_get_request(args) - - elif cmd == 'cp': - paths = args.split() - if len(paths) != 2: - raise ValueError('Неверное количество путей в параметрах.') - - src, dest = paths - - # Если файл не присутствует в БД, то предварительно его загружаем - status = 0 - if src not in db: - status = make_get_request(src) - - if status == 0: - with open(dest, 'wb') as f: - f.write(db[src]) - - else: - raise ValueError('Команда не распознана') +DATATYPE_SIZE = 4 # int +CHUNK_SIZE = 4096000 +HOST, PORT = ADDR = 'localhost', 6000 -def make_get_request(path): - packet = pack_data(json.dumps({'method': 'get', 'path': path})) - sock.sendall(packet) - response = get_response() - return handle_response(response) +class Client: + def __init__(self, server_host, server_port): + self.server = socket.socket() + self.db = shelve.open('backdoor.db') + address = server_host, server_port + cnt = 0 + while cnt < 10: + try: + cnt += 1 + self.server.connect(address) -def parse_response(data): - return json.loads(data.decode()) + except ConnectionRefusedError: + print('Не удалось подключиться к серверу. Следующая попытка через 3 секунды...') + sleep(3) + else: + break -def get_response(): - buffer = b'' - while len(buffer) < DATATYPE_SIZE: - data = sock.recv(DATATYPE_SIZE - len(buffer)) - if not data: - raise ConnectionError('Connection was closed') + else: + print('Не удалось подключиться к серверу через 10 попыток.\nОстанавливаемся...') + self.server.close() - buffer += data + def start(self): + while True: + try: + data = input('Введите команду:\n') + self.handle_input(data) - size_packed, buffer = buffer[:DATATYPE_SIZE], buffer[DATATYPE_SIZE:] - if not size_packed: - raise ConnectionError('Connection was closed') + except ValueError as err: + print(err) - (size,) = struct.unpack('I', size_packed) - data = sock.recv(size - len(buffer)) - return parse_response(buffer + data) + except KeyboardInterrupt: + break + self.server.close() -def download_file(filesize): - data = b'' + def handle_input(self, data): + cmd, args = data.split(' ', maxsplit=1) - if TQDM: - for count in tqdm(range(0, filesize, CHUNK_SIZE)): - (size,) = struct.unpack('I', sock.recv(DATATYPE_SIZE)) - data += sock.recv(size) - else: - count = 0 - while count < filesize: - (size,) = struct.unpack('I', sock.recv(DATATYPE_SIZE)) - data += sock.recv(size) - count += size + if cmd == 'get': + self.download_file(args) - return data + elif cmd == 'cp': + paths = args.split() + if len(paths) != 2: + raise ValueError('Неверное количество путей в параметрах.') + src, dest = paths -def handle_response(data): - if data['status'] == 0: - print(f'Загружаем файл размером {data["size"]} байт...') - file_data = download_file(data['size']) - db[data['path']] = file_data - print('Загрузка завершена') + # Если файл не присутствует в БД, то предварительно его загружаем + if src not in self.db: + self.download_file(src) - elif data['status'] == 1: - print(data['reason']) + # При любых ошибках при загрузке файла download_file выкинет исключение и исполнение + # не дойдет до этого блока, поэтому можно не проверять наличие src в БД + with open(dest, 'wb') as f: + f.write(self.db[src]) - elif data['status'] == 2: - print('Неизвестная ошибка:') - print(data['reason']) + else: + raise ValueError('Команда не распознана') - return data['status'] + def download_file(self, path): + response = self.make_get_request(path) + if response['status'] == 0: + print(f'Загружаем файл размером {response["size"]} байт...') + file_data = self.receive_data(int(response['size']), use_tqdm=True) + self.db[path] = file_data -def try_connect(): - cnt = 0 - sock = socket.socket() - while cnt < 10: - try: - cnt += 1 - sock.connect(ADDR) + print('Загрузка завершена') - except ConnectionRefusedError: - print('Не удалось подключиться к серверу. Следующая попытка через 3 секунды...') - sleep(3) + elif response['status'] == 1: + raise ValueError(response['reason']) - else: - return sock + elif response['status'] == 2: + raise ValueError(f'Неизвестная ошибка: {response["reason"]}') - else: - print('Не удалось подключиться к серверу через 10 попыток.\nОстанавливаемся...') - sock.close() - return + def make_get_request(self, path): + packet = Client.pack_str(json.dumps({'method': 'get', 'path': path})) + self.server.sendall(packet) + return Client.parse_response(self.receive_data(DATATYPE_SIZE)) + def receive_data(self, data_size, use_tqdm=False): + data = b'' -HOST, PORT = ADDR = 'localhost', 6000 -DATATYPE_SIZE = 4 # int -CHUNK_SIZE = 4096000 + # С копипастой надо бы что-то сделать, но я не знаю что + if not use_tqdm or not TQDM: + while len(data) < data_size: + (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) + data += self.server.recv(packet_size) + if not data: + raise ConnectionError('Connection was closed') -sock = try_connect() -if sock is None: - quit() + elif TQDM: + for count in tqdm(range(0, data_size, CHUNK_SIZE)): + (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) + data += self.server.recv(packet_size) + if not data: + raise ConnectionError('Connection was closed') + return data -db = shelve.open('backdoor.db') + @staticmethod + def pack_str(data): + encoded = data.encode() + return struct.pack('I', len(encoded)) + encoded -while True: - try: - data = input('Введите команду:\n') - handle_input(data) + @staticmethod + def parse_response(data): + return json.loads(data.decode()) - except KeyboardInterrupt: - break -sock.close() +if __name__ == '__main__': + client = Client(HOST, PORT) + client.start() diff --git a/day4/task4/client_class.py b/day4/task4/client_class.py deleted file mode 100644 index 854e5e2..0000000 --- a/day4/task4/client_class.py +++ /dev/null @@ -1,134 +0,0 @@ -import socket -from time import sleep -import struct -import json -import shelve - -try: - from tqdm import tqdm - TQDM = True -except ImportError: - print('Для вывода прогресса загрузки файлов установите библиотеку tqdm') - TQDM = False - - -DATATYPE_SIZE = 4 # int -CHUNK_SIZE = 4096000 -HOST, PORT = ADDR = 'localhost', 6000 - - -class Client: - def __init__(self, server_host, server_port): - self.server = socket.socket() - self.db = shelve.open('backdoor.db') - - address = server_host, server_port - cnt = 0 - while cnt < 10: - try: - cnt += 1 - self.server.connect(address) - - except ConnectionRefusedError: - print('Не удалось подключиться к серверу. Следующая попытка через 3 секунды...') - sleep(3) - - else: - break - - else: - print('Не удалось подключиться к серверу через 10 попыток.\nОстанавливаемся...') - self.server.close() - - def start(self): - while True: - try: - data = input('Введите команду:\n') - self.handle_input(data) - - except ValueError as err: - print(err) - - except KeyboardInterrupt: - break - - self.server.close() - - def handle_input(self, data): - cmd, args = data.split(' ', maxsplit=1) - - if cmd == 'get': - self.download_file(args) - - elif cmd == 'cp': - paths = args.split() - if len(paths) != 2: - raise ValueError('Неверное количество путей в параметрах.') - - src, dest = paths - - # Если файл не присутствует в БД, то предварительно его загружаем - if src not in self.db: - self.download_file(src) - - # При любых ошибках при загрузке файла download_file выкинет исключение и исполнение - # не дойдет до этого блока, поэтому можно не проверять наличие src в БД - with open(dest, 'wb') as f: - f.write(self.db[src]) - - else: - raise ValueError('Команда не распознана') - - def download_file(self, path): - response = self.make_get_request(path) - if response['status'] == 0: - print(f'Загружаем файл размером {response["size"]} байт...') - - file_data = self.receive_data(int(response['size']), use_tqdm=True) - self.db[path] = file_data - - print('Загрузка завершена') - - elif response['status'] == 1: - raise ValueError(response['reason']) - - elif response['status'] == 2: - raise ValueError(f'Неизвестная ошибка: {response["reason"]}') - - def make_get_request(self, path): - packet = Client.pack_str(json.dumps({'method': 'get', 'path': path})) - self.server.sendall(packet) - return Client.parse_response(self.receive_data(DATATYPE_SIZE)) - - def receive_data(self, data_size, use_tqdm=False): - data = b'' - - # С копипастой надо бы что-то сделать, но я не знаю что - if not use_tqdm or not TQDM: - while len(data) < data_size: - (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) - data += self.server.recv(packet_size) - if not data: - raise ConnectionError('Connection was closed') - - elif TQDM: - for count in tqdm(range(0, data_size, CHUNK_SIZE)): - (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) - data += self.server.recv(packet_size) - if not data: - raise ConnectionError('Connection was closed') - - return data - - @staticmethod - def pack_str(data): - encoded = data.encode() - return struct.pack('I', len(encoded)) + encoded - - @staticmethod - def parse_response(data): - return json.loads(data.decode()) - - -client = Client(HOST, PORT) -client.start() diff --git a/day4/task4/server.py b/day4/task4/server.py index fb88629..d013b35 100644 --- a/day4/task4/server.py +++ b/day4/task4/server.py @@ -3,6 +3,11 @@ import struct import json +HOST, PORT = ADDR = '0.0.0.0', 6000 +CHUNK_SIZE = 4096000 +DATATYPE_SIZE = 4 # int + + def pack_str(data: str): encoded = data.encode() return struct.pack('I', len(encoded)) + encoded @@ -54,14 +59,15 @@ def handle_request(data, connection): def get_data(connection): buffer = b'' - while len(buffer) < 4: - data = connection.recv(4 - len(buffer)) + # Считываем размер пакета + 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[:4], buffer[4:] + size_packed, buffer = buffer[:DATATYPE_SIZE], buffer[DATATYPE_SIZE:] if not size_packed: raise ConnectionError('Connection was closed') @@ -93,24 +99,27 @@ def handle_connection(connection, address): handle_request(data.decode(), connection) -HOST, PORT = ADDR = '0.0.0.0', 6000 -CHUNK_SIZE = 4096000 -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 ConnectionResetError: - print(f'Connection with {address[0]} was unexpectedly closed') - - except KeyboardInterrupt: - print('Stopping server...') - sock.close() - print('Server stopped') - break +def main(): + 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 ConnectionResetError: + print(f'Connection with {address[0]} was unexpectedly closed') + + except KeyboardInterrupt: + print('Stopping server...') + sock.close() + print('Server stopped') + break + + +if __name__ == '__main__': + main() -- cgit v1.2.3