Не будет работать в фоне. Архитектурно отличия скорее нет, оно есть только в деталях реализации. У меня не получилось с Jami или Tox из-за нестабильности звонков и самого приложения.
Чтобы peer узнал о другом peer, нужен третий участник (в данной статье это ntied-server). Serverless не получится, я даже не знаю, возможно ли здесь достичь serverless, при отсутствии статического белого IP хотя бы у одного участника.
Если участник не в сети, то сообщение попадает в очередь и потом когда-нибудь, когда оба участника в сети, будет доставлено. Здесь нет сервера для таких случаев, я не решал пока такую проблему.
Ничего плохого сказать не хочу. Из моего опыта - нестабильная работа (креши) и проблемы со звонками (неявные разрывы). При неправильном использовании интерфейса - переход в такое состояние, из которого можно выйти только перезапуском приложения. Функция "поделиться экраном" сделана довольно оригинально - через видеозвонки и переопределение в настройках устройства камеры на область экрана. Правда, помимо оригинальности, смотреть демонстрацию было довольно проблематично. Вполне возможно, что за два месяца проблемы исправили, или же проблемы были именно у меня, а не в приложении.
сможешь ли ты сделать лучше не полагаясь целиком и полностью на свой маленький релей сервер
Да, в моем случае уже получилось лучше, чем Jami, в плане работоспособности. Если говорить про OpenDHT, то интеграция возможна, но это задача низкого приоритета, поскольку мой текущий подход уже решает основные проблемы.
Стоит ли заморачиваться с п2п
Стоит, ведь P2P подразумевает прямое соединение между собеседниками (в случаях когда это возможно), которое ограничено только пропускной способностью канала собеседников, а не сервера.
Нигде в статье не указывается, что месенджер анонимный. Анонимность и нормально работающие звонки (в идеале с видеосигналом) - крайне нетривиальная задача.
Есть две проблемы, почему здесь не используется только message_id (который генерируются UUIDv7):
В распределенной системе нет гарантии, что у обоих участников часы синхронизированы.
Сообщение может быть составлено и отправлено на устройстве пользователя в момент плохого соединения или вообще, отсутствия соединения с собеседником. В результате сообщение может быть доставлено со значительной задержкой.
log_id позволяет сериализовать запись каждого сообщения не зависимо от времени, гарантируя что у каждого собеседника история является append-only.
А вы им пользовались? Перед тем как что-то колхозить, были попытки подружиться в том числе и с Jami. К сожалению, с простыми звонками ничего не вышло: у меня и у собеседника были те же проблемы - постоянно надо дергать аудио, чтобы нужное устройство ввода/вывода использовалось, прерывания звонков. Для моих целей Jami как и Tox, не подошел на тот момент.
Когда реализовывал транспорт я смотрел на спецификацию quic чтобы почерпнуть немного идей. Однако руки еще не дошли изучить как обстоят дела с реализацией quic для rust и насколько его реалистично адаптировать к p2p.
Использовалась библиотека sea, но возникли проблемы с линковкой зависимостей под windows. Сейчас там 2 кодека - raw и ADPCM, реализацию можно найти в репозитории.
Я рассматривал libp2p, но на момент рассмотрения у меня не было глубоких знаний (проверенных на практике) и хотелось получить минимальный рабочий вариант без сложных зависимостей, с максимальным контролем и пониманием происходящих процессов. В текущей реализации есть один изьян - соединения не работают при включенном VPN (возможно у меня такой попался, но это вроде распространенный случай). Сейчас планирую добавить TURN, это должно покрыть все возможные случаи, а дальше уже понять, что нужно ли переходить на libp2p (библиотека очень обширная).
Насчет создания нужного чата - нужно знать публичный ключ собеседника (точнее хеш от ключа). Дальше собеседник решит принять от вас запрос или нет. Тут да, как в tox или jami. Метаинформация о контактах нигде, кроме как между собеседниками не хранится, и искать другими способами не получится.
Плюсы в больших проектах не сильно быстрее на этапе компиляции. По моим ощущениям даже медленнее (возможно из-за отсутствия модулей, большого числа сложных шаблонов или еще чего-то). А дебаг, почему не компилируется огромный плюсовый проект крайне "приятный".
О каких уникальных фичах идет речь? Поиск по ключу, вставка, удаление, итерирование есть в обычном map[K]V. Упорядочивание по компаратору в каком-нибуть BTree, RBTree или AVLTree есть.
Можно сказать, что это своего рода гибрид между списком и деревом, только без всяких заморочек.
Хотелось бы конкретики. Открыл код, по сравнению с листом - код выглядит сложнее. По сравнению с деревьями поиска +- одинаково, может чуть проще.
Как итог:
Никакого описания принципа работы.
Отсутствие оценки скорости работы "уникальных операций" (хотя бы асимптотическая оценка).
Отсутствие примеров, когда данная структура может дать преимущество над общеизвестными бинарными деревьями поиска и хеш-таблицами.
Для enum конечно еще придется попотеть, чтобы интерфейс Foo руками реализовывать не пришлось:
// А еще надо реализовать сам derive(Foo).
#[derive(Foo)]
enum MyEnum {
A(TypeA), // TypeA реализует Foo
B(TypeB), // TypeB реализует Foo
}
В любом случае, да, в Rust подобное невыразимо. Плохо ли это? Лично мне такая возможность в C++ и не нравится, так как за нее нужно платить. Например, чтением сотен строк непонятных ошибок, с чем я периодически сталкиваюсь и могу застрять на часок другой.
Вообще, на тему достоинств и недостатков различных моделей дженериков (а у Rust и C++ они различаются) есть хорошая статья.
error[E0004]: non-exhaustive patterns: `MyEnum::NullInt(MyOption::None)` not covered
--> src/main.rs:73:11
|
73 | match v {
| ^ pattern `MyEnum::NullInt(MyOption::None)` not covered
|
note: `MyEnum` defined here
--> src/main.rs:67:6
|
67 | enum MyEnum {
| ^^^^^^
68 | Int(i64),
69 | NullInt(MyOption<i64>),
| ------- not covered
= note: the matched value is of type `MyEnum`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
75 ~ MyEnum::NullInt(MyOption::Some(v)) => {},
76 + MyEnum::NullInt(MyOption::None) => todo!()
|
Да, опять случай конечно не совсем реальный, но все же с вами не соглашусь. На реальный можно глянуть например сюда и сюда. На более сложных примерах я вообще удивляюсь, как подробно и понятно компилятор объясняет почему я не прав. Да, в редких случаях может выдать что-то не очевидное (как-то с GAT имел дело, та еще мистика, особенно когда там еще с lifetime-ми надо поработать).
и тут даже не нужен визит,
Да, в этом примере может и не нужен, но это уже из-за того что пример построен на стандартных enum-ах.
в visit компилятор заставит обработать все случаи
Интересно увидеть, как будет выглядеть ошибка. Сколько сотен строк с объяснениями как компилятор пытался подставить типы в шаблоны, да ничего не вышло. Вполне возможно что в современном C++20 это выглядит читаемо, поэтому интересно увидеть какой-нибудь такой пример.
Мне интересно, как будет выглядеть на variant+visit такой простой пример:
fn do_smth(v: Result<Option<i64>, String>) {
match v {
Ok(Some(v)) => {}
Ok(None) => {}
Err(v) => {}
}
}
А еще интересно, как будет выглядеть вывод компилятора, когда я забуду указать один из случаев.
Например как будет выглядеть ошибка, если я удалю ветку с Err(v) => {}:
error[E0004]: non-exhaustive patterns: `Err(_)` not covered
--> main.rs:12:11
|
12 | match v {
| ^ pattern `Err(_)` not covered
|
note: `Result<Option<i64>, std::string::String>` defined here
--> /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:502:1
|
502 | pub enum Result<T, E> {
| ^^^^^^^^^^^^^^^^^^^^^
...
511 | Err(#[stable(feature = "rust1", since = "1.0.0")] E),
| --- not covered
= note: the matched value is of type `Result<Option<i64>, std::string::String>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
14 ~ Ok(None) => {},
15 ~ Err(_) => todo!()
|
Наверное не все просто. Если продолжать размышлять в таком ключе, то видимо можно придти к тому, что именно банки должны нести полную ответственность (не путать с банкой).
Не будет работать в фоне. Архитектурно отличия скорее нет, оно есть только в деталях реализации. У меня не получилось с Jami или Tox из-за нестабильности звонков и самого приложения.
Чтобы peer узнал о другом peer, нужен третий участник (в данной статье это ntied-server). Serverless не получится, я даже не знаю, возможно ли здесь достичь serverless, при отсутствии статического белого IP хотя бы у одного участника.
Если участник не в сети, то сообщение попадает в очередь и потом когда-нибудь, когда оба участника в сети, будет доставлено. Здесь нет сервера для таких случаев, я не решал пока такую проблему.
Ничего плохого сказать не хочу. Из моего опыта - нестабильная работа (креши) и проблемы со звонками (неявные разрывы). При неправильном использовании интерфейса - переход в такое состояние, из которого можно выйти только перезапуском приложения. Функция "поделиться экраном" сделана довольно оригинально - через видеозвонки и переопределение в настройках устройства камеры на область экрана. Правда, помимо оригинальности, смотреть демонстрацию было довольно проблематично. Вполне возможно, что за два месяца проблемы исправили, или же проблемы были именно у меня, а не в приложении.
Да, в моем случае уже получилось лучше, чем Jami, в плане работоспособности. Если говорить про OpenDHT, то интеграция возможна, но это задача низкого приоритета, поскольку мой текущий подход уже решает основные проблемы.
Стоит, ведь P2P подразумевает прямое соединение между собеседниками (в случаях когда это возможно), которое ограничено только пропускной способностью канала собеседников, а не сервера.
Нигде в статье не указывается, что месенджер анонимный. Анонимность и нормально работающие звонки (в идеале с видеосигналом) - крайне нетривиальная задача.
Есть две проблемы, почему здесь не используется только message_id (который генерируются UUIDv7):
В распределенной системе нет гарантии, что у обоих участников часы синхронизированы.
Сообщение может быть составлено и отправлено на устройстве пользователя в момент плохого соединения или вообще, отсутствия соединения с собеседником. В результате сообщение может быть доставлено со значительной задержкой.
log_id позволяет сериализовать запись каждого сообщения не зависимо от времени, гарантируя что у каждого собеседника история является append-only.
Запустить приложение можно так:
cargo run --bin ntiedЗа лицензирование отдельное спасибо, обязательно разберусь.
А вы им пользовались? Перед тем как что-то колхозить, были попытки подружиться в том числе и с Jami. К сожалению, с простыми звонками ничего не вышло: у меня и у собеседника были те же проблемы - постоянно надо дергать аудио, чтобы нужное устройство ввода/вывода использовалось, прерывания звонков. Для моих целей Jami как и Tox, не подошел на тот момент.
Когда реализовывал транспорт я смотрел на спецификацию quic чтобы почерпнуть немного идей. Однако руки еще не дошли изучить как обстоят дела с реализацией quic для rust и насколько его реалистично адаптировать к p2p.
Использовалась библиотека sea, но возникли проблемы с линковкой зависимостей под windows. Сейчас там 2 кодека - raw и ADPCM, реализацию можно найти в репозитории.
Я рассматривал libp2p, но на момент рассмотрения у меня не было глубоких знаний (проверенных на практике) и хотелось получить минимальный рабочий вариант без сложных зависимостей, с максимальным контролем и пониманием происходящих процессов. В текущей реализации есть один изьян - соединения не работают при включенном VPN (возможно у меня такой попался, но это вроде распространенный случай). Сейчас планирую добавить TURN, это должно покрыть все возможные случаи, а дальше уже понять, что нужно ли переходить на libp2p (библиотека очень обширная).
Насчет создания нужного чата - нужно знать публичный ключ собеседника (точнее хеш от ключа). Дальше собеседник решит принять от вас запрос или нет. Тут да, как в tox или jami. Метаинформация о контактах нигде, кроме как между собеседниками не хранится, и искать другими способами не получится.
В принципе, а почему бы и сейчас не открыть. Точно не фейк, можно проверить
Плюсы в больших проектах не сильно быстрее на этапе компиляции. По моим ощущениям даже медленнее (возможно из-за отсутствия модулей, большого числа сложных шаблонов или еще чего-то). А дебаг, почему не компилируется огромный плюсовый проект крайне "приятный".
О каких уникальных фичах идет речь? Поиск по ключу, вставка, удаление, итерирование есть в обычном map[K]V. Упорядочивание по компаратору в каком-нибуть BTree, RBTree или AVLTree есть.
Хотелось бы конкретики. Открыл код, по сравнению с листом - код выглядит сложнее. По сравнению с деревьями поиска +- одинаково, может чуть проще.
Как итог:
Никакого описания принципа работы.
Отсутствие оценки скорости работы "уникальных операций" (хотя бы асимптотическая оценка).
Отсутствие примеров, когда данная структура может дать преимущество над общеизвестными бинарными деревьями поиска и хеш-таблицами.
Пост об утилите на 10 простых строчек, да и еще с ошибками в одном предложении ("простую версия"). Зачем, для кого, почему...
Нужно считать по байтам:
2(порт источника)+2(порт назначения)+4(номер последовательности)+4(номер подтверждения)+1(длина заголовка и часть резерва).
Верно. Хотя можно сделать что-то альтернативное, но это все же не то, что вам надо:
Для enum конечно еще придется попотеть, чтобы интерфейс
Fooруками реализовывать не пришлось:В любом случае, да, в Rust подобное невыразимо. Плохо ли это? Лично мне такая возможность в C++ и не нравится, так как за нее нужно платить. Например, чтением сотен строк непонятных ошибок, с чем я периодически сталкиваюсь и могу застрять на часок другой.
Вообще, на тему достоинств и недостатков различных моделей дженериков (а у Rust и C++ они различаются) есть хорошая статья.
Возможно, хотя встречается и не сказать что редко.
Точно не смогу? Вроде смог:
Хорошо, удаляем
MyEnum::NullInt(OptionI64::None):Да, опять случай конечно не совсем реальный, но все же с вами не соглашусь. На реальный можно глянуть например сюда и сюда. На более сложных примерах я вообще удивляюсь, как подробно и понятно компилятор объясняет почему я не прав. Да, в редких случаях может выдать что-то не очевидное (как-то с GAT имел дело, та еще мистика, особенно когда там еще с lifetime-ми надо поработать).
Да, в этом примере может и не нужен, но это уже из-за того что пример построен на стандартных enum-ах.
Интересно увидеть, как будет выглядеть ошибка. Сколько сотен строк с объяснениями как компилятор пытался подставить типы в шаблоны, да ничего не вышло. Вполне возможно что в современном C++20 это выглядит читаемо, поэтому интересно увидеть какой-нибудь такой пример.
Мне интересно, как будет выглядеть на variant+visit такой простой пример:
А еще интересно, как будет выглядеть вывод компилятора, когда я забуду указать один из случаев.
Например как будет выглядеть ошибка, если я удалю ветку с
Err(v) => {}:Наверное не все просто. Если продолжать размышлять в таком ключе, то видимо можно придти к тому, что именно банки должны нести полную ответственность (не путать с банкой).