Протокол передачи
Протокол — это правила общения между устройствами. Как в разговоре: сначала здороваешься, потом задаёшь вопрос, потом получаешь ответ. Если нарушить порядок — собеседник тебя не поймёт.
В csend есть два протокола: один для обнаружения (Discovery), другой для передачи файлов (Transfer). Оба используют одну и ту же «трубу» — libp2p request-response поверх JSON.
Идентификатор протокола
/kontinuum/send/1.0.0Все устройства с csend говорят на этом «языке». Если кто-то использует другой протокол — они просто не поймут друг друга (и проигнорируют).
Типы сообщений
Все сообщения описаны одним перечислением Message. Каждое сообщение может быть как запросом, так и ответом — libp2p обрабатывает их одинаково.
Роли Discovery-сообщений
| Сообщение | Кто отправляет | Зачем |
|---|---|---|
WhoWantsToSend | Потенциальный получатель | «Есть ли у кого файлы для меня?» — запрос раздач от всех пиров |
AdvertiseSend | Отправитель (ответ) | «Да, у меня есть файлы» — ответ на WhoWantsToSend (используется при quiet = false) |
WhoWantsToReceive | Отправитель | «Я раздаю файлы, кто хочет принять?» — объявление раздачи всем пирам |
AdvertiseReceive | Потенциальный получатель (ответ) | «Я здесь, готов принимать» — подтверждение присутствия |
NoLongerSending | Отправитель | «Я больше не раздаю эти файлы» — отмена конкретной раздачи |
Два направления Discovery:
WhoWantsToReceive— отправитель объявляет свои файлы.WhoWantsToSend— получатель спрашивает, у кого есть файлы. Оба механизма работают одновременно, чтобы устройства находили друг друга независимо от порядка подключения к сети.
Поток Discovery — обнаружение устройств
Discovery работает постоянно в фоне, пока открыт TUI. Его задача — показывать, кто в сети хочет отправить файлы.
Важно: в Discovery не передаётся ни кодовая фраза, ни её хеш. Только публичные метаданные: имя устройства, метка передачи, список файлов и размер. transfer_id — случайный идентификатор, не связанный с кодовой фразой.
Поток WhoWantsToSend — обратное обнаружение
Когда устройство подключается к сети и хочет узнать, кто раздаёт файлы, оно отправляет WhoWantsToSend:
Этот механизм дополняет основной поток: если Боб подключился к сети после того, как Алиса начала раздачу, introduce_to_new_peer() автоматически отправит ему все активные раздачи. Но если Алиса не заметила Боба через mDNS (редкий случай), WhoWantsToSend позволяет Бобу запросить информацию самостоятельно.
Ограничение списка файлов
В Discovery-сообщениях список файлов обрезается до 100 записей (MAX_DISCOVERY_FILES). Это нужно, чтобы сообщение не стало слишком большим. Полный список файлов передаётся позже, в сообщении Offer при рукопожатии.
Поток Transfer — передача файлов
Transfer — это последовательность шагов, которая запускается после того, как получатель ввёл кодовую фразу и устройства нашли друг друга.
Подробности о чанках (Chunk)
Файлы передаются не целиком, а кусочками (chunks) по 256 КиБ (262 144 байта). Это нужно по нескольким причинам:
| Причина | Объяснение |
|---|---|
| Прогресс | Можно показывать процент выполнения |
| Память | Не нужно загружать весь файл в оперативную память |
| Надёжность | Если что-то сломалось, потеряется только один кусочек |
| Шифрование | Каждый кусочек шифруется отдельно с уникальным nonce |
Структура чанка:
Chunk {
file_index: 0, // Какой файл (индекс в списке)
offset: 262144, // Смещение в файле (в байтах)
data: [...] // Зашифрованные данные (до 256 КиБ + 16 байт тег)
}Как работает подтверждение (Ack)
После каждого чанка получатель отправляет Ack — подтверждение, что данные получены. Отправитель ждёт Ack перед отправкой следующего чанка:
Несколько файлов
Если отправляется несколько файлов, они передаются последовательно — один за другим. file_index указывает, к какому файлу относится чанк:
Chunk { file_index: 0, offset: 0, data: [...] } # photo1.jpg, начало
Chunk { file_index: 0, offset: 262144, data: [...] } # photo1.jpg, продолжение
Chunk { file_index: 1, offset: 0, data: [...] } # photo2.jpg, начало
...
DoneРазмеры сообщений
JSON-кодек libp2p имеет ограничения на размер сообщений:
| Параметр | Лимит | Зачем |
|---|---|---|
| Макс. размер запроса | 16 MiB | Chunk (256 КиБ данных → ~1 MiB в JSON) |
| Макс. размер ответа | 16 MiB | Offer с большим списком файлов |
| Таймаут запроса | 600 сек (10 мин) | Медленные соединения |
Почему JSON?
Vec<u8>в JSON сериализуется как массив чисел:[72, 101, 108, 108, 111]. Это неэффективно (каждый байт занимает ~3.5 символа), но просто для отладки. В будущем можно перейти на бинарный формат для скорости.
Сообщение Error
Error { message } используется для сигнализации ошибок во время передачи — например, если отправитель не может прочитать файл с диска или получатель не может записать данные. При получении Error передача прерывается, а сообщение показывается пользователю.
FileInfo — метаданные файла
Каждый файл описывается структурой FileInfo:
pub struct FileInfo {
pub name: String, // "photos/cat.jpg" (путь относительно корня)
pub size: u64, // 1048576 (размер в байтах)
}Для директорий имя включает путь: если отправляешь папку photos/, файл внутри будет называться photos/cat.jpg, а не просто cat.jpg.