Страницы

Поиск по вопросам

среда, 12 июня 2019 г.

Как лучше реализовать коммуникацию по сети в .NET в полной связке?

Добрый день,
Есть программа на .Net, которая общается со своими копиями по сети с помощью "зачаток WCF".
Ближе всего это находится к топологии сети "Полносвязная". Т.е. каждая программа непосредственно связана со всеми остальными. Это нам нужно т.к. мы посылаем данные непосредственно в обход сервера. Нам нужны небольшие задержки, а сервер это лишний скачок. Шина тоже подойдет.
На данный момент у нас есть абстрактный сервер, экземпляр который должен запускаться раньше всех, и клиенты. Это определяется настройками. При подсоединении к серверу клиент получает адреса других подключенных клиентов и потом может уже говорить с ними напрямую. Что происходит когда прога закрыта не интересно.
Собственно проблемы думаю ясны :)
прога-сервер падает/ перезагружается и все плохо. Очень поздно ловим дисконнекты всех. еще куча мелочей.
Хотелось бы альтернативы. Кто как решал такую проблему? Мы не можем быть уникальными :)
На данный момент есть идеи реализовать heartbeat (пакет каждую секунду и ждем ответа.) или писать жуть на UDP server/client классах.


Ответ

Жуть на UDP писать не надо, хотя UDP, безусловно, понадобится для оповещения уже работающих хостов о появлении в сети нового участника, если вы хотите полностью избавиться от сервера.
Топология "каждый с каждым" подразумевает, что сеть состоит из универсальных хостов, каждый из которых одновременно является и клиентом (может подключаться к другим хостам), и сервером (может обрабатывать подключения других хостов).
Это легко организовать на основе WCF. Для комфортной работы понадобится дуплексный канал связи, который можно организовать несколькими способами:
wsDualHttpBinding - Главный недостаток, это низкая скорость реакции на аварийные ситуации и отсутствие контроля за состоянием каналов связи. Обнаружить "упавший" хост можно только в момент обращения к нему, никаких встроенных средств не предусмотрено. Это связано стем, что он построен на основе протокола прикладного уровня HTTP, который, в общем-то, и не обязан и не может гарантировать устойчивую связь, тем более двустороннюю, не его это уровень, он выше этого =). netTcpBinding - высокоуровневая реализация обмена данными на основе протокола транспортного уровня TCP, который как раз и предоставляет полный контроль, поддерживает сессии, heartbeat и т.д. Данный тип привязки WCF может работать в одностороннем и двустороннем режиме в зависимости от конфигурации и реализации самого сервиса. На нем и остановимся.
Теперь разберемся с конфигурацией нашего "универсального хоста". Как было сказано выше, он должен выполнять две функции одновременно. Для иллюстрации сделаем примитивный мессенджер для локальной сети совсем без сервера (почему только для локальной).
Для начала научим сообщать активным участникам сети о своем присутствии и слушать вновь пришедших. Для этого понадобится совсем немного:
Заводим необходимые поля в классе хоста будущего сервиса (добавьте static по вкусу или необходимости)
private readonly int port = 8888; private readonly IPEndPoint listenedAddress = new IPEndPoint(IPAddress.Any, port); private readonly IPEndPoint addressForSend = new IPEndPoint(IPAddress.Broadcast, port); private readonly UdpClient udpClient = new UdpClient() настраиваем UdpClient
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); udpClient.ExclusiveAddressUse = false; //только для работы на одной машине, в нормальных условиях не требуется udpClient.Client.Bind(listenedAddress); Отправляем всем слушателям свои контактные данные. Так как UDP не требует подтверждения получения пакета и данных не много, используем синхронный метод.
//адрес пока просто для примера, позже заменим на реальный var bytes = Encoding.UTF8.GetBytes("net.tcp://localhost:9995/chat"); udpClient.Send(bytes, bytes.Length, addressForSend); Включаем ожидание входящих запросов от новых клиентов. Для того чтобы не городить костыли из бесконечных циклов и Thread/Task, воспользуемся готовым асинхронным методом:
udpClient.BeginReceive(new AsyncCallback(ReceiveCallback), null);
При получении входящего пакета будет вызван callback-метод, пишем его:
private void ReceiveCallback(IAsyncResult ar) { //в sender попадет адрес отправителя пакета IPEndPoint sender = listenedAddress; byte[] receiveBytes = udpClient.EndReceive(ar, ref sender); //Это условие нужно только для работы на одной машине, чтобы не обрабатывать "эхо" //мы же слушаем все адреса, значит и свой тоже. Не самое красивое решение, но рабочее. if(sender.Address.ToString() != "0.0.0.0") { string receiveString = Encoding.ASCII.GetString(receiveBytes); //тут позже будем что-то делать с полученными данными нового участника сети } //Ну и запускаем прослушивание заново udpClient.BeginReceive(new AsyncCallback(ReceiveCallback), null); }
Как видите, удалось довольно красиво обойтись без "классических" while(true). Больше похоже на событийную модель, со своими оговорками разумеется.
Теперь займемся непосредственно WCF компонентой. Приводить полный листинг кода тут я не буду, покажу только основные моменты, на которые стоит обратить особое внимание, т.к. в принципе WCF довольно неплохо документирован.

Продолжение следует...

Комментариев нет:

Отправить комментарий