Простой способ передать большие файлы через socket на Python

Python предлагает очень удобную обертку над сокетами (socket), однако, как известно, сокеты являются достаточно низкоуровневыми и не гарантируют доставку сообщения целиком. То есть, отправив большой объем данных, вы их получите, но, скорее всего, по частям. В этой заметке будет показан простой способ сделать обертку над сокетами для получения примитивного протокола уровня пакетов и передавать таким образом большие файлы.

Идея

Фрагментация сообщений при передаче через сокеты приводит к тому, что невозможно заранее знать размер получаемого сообщения, а прием/передача с применением заведомо большего буфера также не гарантирует передачу за один вызов socket.send.

Простейшим решением этой проблемы является добавление в начало посылки ее размера в байтах. Таким образом, первым вызовом recvall можно получать только размер (4 байта), который практически гарантированно будет передан в одном TCP пакете (фрагментация, как правило, возникает при передаче больше, чем 1 кБ), после чего считать из буфера оставшееся сообщение, длинна которого уже известна.

Передача пакета

Для передачи большого сообщения (например, картинки) к нему, сформированным привычным образом (как для socket) добавим в начало длину в байтах (в сетевом порядке следования байт) и отправим целиком через вызов socket.send.

Реализуется это простой функцией:

Получение пакета

Получение пакета менее тривиально: первоначально напишем вспомогательную функцию для получения заданного числа байт:

Так, пока не будет достигнут заданный размер, в byteArray добавляются принятые данные. По достижению заданного размера возвращается весь принятый пакет.

Теперь можно написать функцию для приема пакета целиком:

Тут первоначально из сокета считывается размер сообщения (4 байта), распаковывается в целое число и, узнав таким образом размер сообщения, получается остаток данных с помощью вспомогательной функции

Простой класс

Упростим использование: чтобы не передавать сокет каждый раз явно, создадим класс.

Фактически он ничем не отличается от предложенных ранее функций, но объект сокета передается один раз при создании, а не каждый раз при вызове функций:

Поделиться

Добавить комментарий