summaryrefslogtreecommitdiff
path: root/day4/task4/server.py
blob: fb886292618a8fcff2c839b9274c51dbc346f2e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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': 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) < 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 = 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