summaryrefslogtreecommitdiff
path: root/day4/task4/server.py
blob: d013b35afb3c2858a1fa512feb568a8c78c9f4a4 (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
117
118
119
120
121
122
123
124
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()