Архитектура
Программа csend состоит из нескольких модулей, каждый из которых отвечает за свою задачу. Представь это как конструктор LEGO — каждый кубик делает что-то одно, а вместе они образуют целое приложение.
Общая схема модулей
Что делает каждый модуль
main.rs — Диспетчер
Как охранник на входе: смотрит, что ты набрал в командной строке, и направляет тебя в нужное место.
csend send photo.jpg → вызывает send.rs
csend recv 3-royal... → вызывает receive.rs
csend tui → вызывает tui/mod.rs
csend (без аргументов) → вызывает tui/mod.rsТакже настраивает логирование: обычно выводит предупреждения и ошибки (уровень warn), с флагом --verbose — всё подряд (уровень debug).
code.rs — Генератор кодовых фраз
Создаёт и обрабатывает кодовые фразы вроде 3-royal-mount-dance.
| Функция | Что делает | Пример |
|---|---|---|
generate_code() | Генерирует случайную фразу | "7-castle-river-bloom" |
code_to_room_id() | Хеширует фразу → ID комнаты | "a3f8b2c1e9d04567" |
code_to_encryption_key() | Выводит ключ шифрования | [u8; 32] (256 бит) |
protocol.rs — Словарь сообщений
Определяет все типы сообщений, которыми обмениваются устройства. Это как словарь — оба устройства должны говорить на одном языке.
Подробнее: Протокол передачи
send.rs — CLI-отправка
Режим csend send файл1 файл2: создаёт P2P-рой, генерирует кодовую фразу, показывает её в терминале и ждёт подключения получателя. После завершения передачи процесс автоматически завершается.
receive.rs — CLI-приём
Режим csend recv 3-royal-mount-dance: создаёт P2P-рой, ищет отправителя через mDNS (таймаут — 60 секунд), проводит рукопожатие и принимает файлы в текущую директорию.
network.rs — Строитель сети
Собирает libp2p-«рой» (swarm) — штуку, которая умеет находить соседей и обмениваться сообщениями. Настраивает два поведения (SendBehaviour): mDNS для обнаружения и request-response для обмена JSON-сообщениями. Все ограничения (16 MiB на сообщение, 600 с таймаут) задаются здесь.
transfer.rs — Мотор передачи
Собирает файлы (collect_files()), шифрует чанками по 256 КиБ (encrypt_chunk()), отправляет и принимает с расшифровкой (decrypt_chunk()). Использует трейт TransferProgress для отчёта о прогрессе — в CLI это вывод в консоль, в TUI — обновление прогресс-бара.
Подробнее: Безопасность
Как модули общаются
Модули не вызывают друг друга напрямую (это создало бы «спагетти-код»). Вместо этого они общаются через каналы — как почтовые ящики.
Каждый канал — это пара:
- Отправитель (tx) — кладёт сообщение в ящик
- Получатель (rx) — забирает сообщение из ящика
Типы каналов:
| Канал | Направление | Что передаёт |
|---|---|---|
DiscoveryCommand | App → DiscoveryTask | «Объяви файлы», «Убери объявление», «Обнови» |
DiscoveryEvent | DiscoveryTask → App | «Нашёл отправителя», «Нашёл получателя», «Отправитель ушёл» |
P2pCommand | Pane → P2P Task | «Принять», «Отклонить», «Завершить» |
P2pEvent | P2P Task → Pane | «Подключён», «Прогресс 45%», «Готово» |
Паттерн «Outbox» — очередь уведомлений
Панели (SendPane, ReceivePane, NavigatorPane) не могут напрямую управлять фоновыми задачами. Вместо этого они складывают уведомления в «outbox» (почтовый ящик), а App забирает их на каждом тике:
Главный цикл TUI
TUI работает как часы — каждые 80 миллисекунд (12.5 раз в секунду) выполняется один «тик»:
Параллельные «рои» (Swarms)
Ключевой архитектурный момент: у каждого устройства работает несколько отдельных libp2p-роёв одновременно. Каждый рой — независимая P2P-сеть с собственным ID.
Почему отдельные рои?
- Изоляция — долгая передача (например, фильма на 4 ГБ) не блокирует обнаружение новых устройств. Discovery продолжает работать, пока P2P-рой занят чанками.
- Параллелизм — можно одновременно отправлять файлы нескольким получателям, каждый в своём рое.
- Чистый жизненный цикл — P2P-рой создаётся при начале передачи и уничтожается после неё, освобождая ресурсы (порт, mDNS-запись, память).
- Discovery Swarm — живёт всегда, пока TUI открыт. Объявляет наши файлы, слушает чужие.
- P2P Swarm — создаётся на каждую передачу и умирает после неё. Занимается тяжёлой работой: рукопожатие, шифрование, передача чанков.