diff options
| author | Андрей Гущин <saintruler@gmail.com> | 2019-06-06 10:01:07 +0300 |
|---|---|---|
| committer | Андрей Гущин <saintruler@gmail.com> | 2019-06-06 10:01:07 +0300 |
| commit | dfbfd62186cb94abd79a2da3756fb6fe7d66f3dd (patch) | |
| tree | 72c1874581c490e65f87113cf7c0ffb43cb34f0b /day4/task4/server.py | |
| parent | f3512425265d2432c1791850fee3fa313267432b (diff) | |
| parent | 7f2e25edde7c4e8bc60514156abf28014a3f0c3c (diff) | |
Merge branch 'day4_task4' into 'master'
День 4, задача 4
See merge request saintruler/trainee!24
Diffstat (limited to 'day4/task4/server.py')
| -rw-r--r-- | day4/task4/server.py | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/day4/task4/server.py b/day4/task4/server.py new file mode 100644 index 0000000..d013b35 --- /dev/null +++ b/day4/task4/server.py @@ -0,0 +1,125 @@ +import socket +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 + + +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': 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): + 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 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) + + +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() |