Вы находитесь на странице: 1из 26

Министерство образования республики Молдова

Технический университет Молдовы


Кафедра «Автоматика и информационные технологии»

Отчёт

Лабораторная работа № 1-2


Тема: Агент обмена сообщениями

выполнил студент гр. TI-124 Брынзан Л.


проверил: Чорбэ Д.

Кишинёв 2015
Содержание
Цель работы ...2
Задание ...2
Краткие факты о шаблонах проектирования агентов обмена сообщениями ...2
Реализация чат-приложения ...4
Вывод ...13
Список литературы ...14
Приложение ...15

1
Цель работы
Интегрирование, основанное на агентах обмена сообщениями, позволяющими
асинхронный обмен данными между распределёнными компонентами системы.

Задание
Определить рабочий протокол агента обмена сообщениями: формат сообщений
(XML), количество каналов, структура сообщения, политика доставки для различных
случаев. Разработать абстрактный слой общения на уровне сети: транспортный протокол,
параллельная обработка запросов. Разработать элементы, обеспечивающие хранение
полученных сообщений: потокобезопасные коллекции или сериализация. Разработать
абстрактный слой маршрутизации сообщений.

Краткие факты о шаблонах проектирования агентов обмена сообщениями


Данная работа строится вокруг реализации агента сообщений (Message Broker),
который является физическим компонентом системы, призванным обрабатывать
коммуникацию между приложениями[5]. Вместо того, чтобы сообщаться друг с другом
напрямую, приложения отправляют все сообщения брокеру, передавая с ними логическое
имя получателя сообщения. Брокер ищет приложения под таким логическим именем и
отправляет сообщения им (см. рисунок 1).

Рисунок 1 - Схема работы агента сообщений[5]

Брокер также предлагает различные интерфейсы приложениям и работает


переводчиком сообщений из одного интерфейса в другой при необходимости. Таким
образом, брокер позволяет создать слабую привязаннось приложений друг к другу. Он
позволяет изменять конфигурацию системы без затрагивания кода приложений. Также,
брокер может выбирать приложения, с которыми он работает, в зависимости от их
качества обслуживания и передачи.
Существует несколько шаблонов, которые внедряют функциональность брокера в
приложение (Content-Dispatcher-Server, Content-Based Router, Mediator и т.д.). Брокер,
разработанный для данного отчета, соответствует следующим шаблонам[1].
Message Channel соединяет приложения, принимая и переадресовывая сообщения
от одних приложений к другим, и строится на базе очереди сообщений (см. рисунок 2).

2
Рисунок 2 - Message Channel[1]

Content-Based Router выбирает получателя в зависимости от данных, содержащихся


в сообщении (см. рисунок 3).

Рисунок 3 - Content-Based Router[1]

Также данная работа может поддерживать модели Dead Letter Channel (так как
некоторые сообщения невозможно передать, если получатель не находится в сети, см.
рисунок 4),

Рисунок 4 - Dead Letter Channel[1]

Publish-Subscribe Channel (есть возможность предавать сообщения сразу


нескольким получателям с помощью протокола UDP, см. рисунок 5)[2].

Рисунок 5 - Publish-Subscribe Channel[1]

3
Реализация чат-приложения
Система, разработанная для данного отчета, представляет собой клиент-серверную
пару, где роль сервера выполняет агент обмена сообщениями (message broker). Несколько
клиентов могут быть одновременно обслужены агентом, который является посредником в
процессе обмена. Он перехватывает сообщения, собирая их в очередь, обрабатывает
данные об отправителе и получателе и передаёт сообщение дальше по цепи, используя
информацию о получателе и отправителе. Данная информация вместе с самим
сообщением передаётся агенту в виде XML-файла (пакета). Принимая пакет, агент его
обрабатывает с помощью метода LoadXML класса XMLDocument, выделяет необходимые
данные, создаёт объект MessageObject (см. листинг 1).

Листинг 1 - Класс для хранения пакетов

Создав объект агент заполняет его полученными данными и помещает в очередь


сообщений. На листинге 2 показана структура агента (брокера).

Листинг 2 - Структура брокера

4
Брокер является консольным приложением. Основу приложения составляет метод
StartListener, который содержит код, принимающий сообщения от клиентов и
сохраняющий как информацию о клиентах, так и сами пакеты в виде объектов (см.
листинг 3).

Листинг 3 - Метод StartListener

Для передачи и приёма сообщений используется протокол UDP[4]. Главный цикл


агента блокирует приложение до тех пор, пока сокетом не будет получено сообщение от
одного из клиентов. Сообщение имеет следующий формат:
<Data>
<To></To>
<From></From>
<Port></Port>
<Message></Message>
</Data>

5
Получив сообщение агент сохраняет данные в объекте типа MessageObject,
добавляет данный объект в очередь сообщений и отправляет первое сообщение в очереди
адресату.
Следует отметить шаг добавления адресанта и порта, с ним ассоциирующегося, в
базу данных агента. Это делается для того, чтобы далее агент знал, по какому порту
связаться с нужным клиентом. Каждый клиент при инициализации регистрируется у
агента, таким образом агент знает обо всех клиентах в сети.
Отправка сообщений осуществляется в методе SendMessage (см. листинг 4).

Листинг 4 - Метод SendMessage

На основе заданных параметров метод строит XML-файл и передаёт его с


помощью протокола UDP заданному клиенту. На листинге 5 показана базовая структура
клиента.

6
Листинг 5 - Структура клиента

Клиент также состоит из двух частей: следящей за сообщениями и отправляющей


сообщения. Так как клиент является приложением Windows Form, его главный поток
занят обработкой графического интерфейса, и все задачи, напрямую не связанные с GUI,
необходимо переносить на другие потоки[6]. Так listener сообщений запускается в
отдельном потоке при инициализации главной формы[7].
Другим важным моментом является задание порта клиента. Ввиду того, что два
приложения не могут занимать один порт, клиент должен работать на порте выше 1024.
Дополнительной трудностью при разработке приложения стало то, что несколько
клиентов запускалось на одном компьютере, что требовало задания уникальных портов в
ходе работы приложения. Для этого был написан метод AssignPort (см. листинг 6).

Листинг 6 - Метод Assign Port

Данный метод выбирает псевдослучайное число из диапазона 2000-4999 и


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

7
бесперебойной работы системы (до тех пор, пока другое приложение не попытается
занять один из выданных таким образом портов).
Listener выполнен аналогично описанному выше обработчику агента.
Единственным отличием является то, что он не может отображать полученную
информацию на форме, так как работает на второстепенном потоке. Для решения этой
проблемы необходимо использовать один из thread-safe методов обращения к
графическому интерфейсу. Для данной работы была выбрана делегация (см. листинг 7)[8].

Листинг 7 - Метод StartListenerAsync

Делегат ShowReceivedMessage вызывается в главном потоке с помощью метода


Control.Invoke и отображает нужную информацию (см. листинг 8).

Листинг 8 - Делегат ShowReceivedMessage

8
При запуске клиента пользователю необходимо ввести своё имя и
зарегистрироваться у брокера. Обработчик, выполняющий это, показан на листинге 9.

Листинг 9 - Обработчик события loginButton_Click

Сама регистрация (см. выше) обрабатывается в отдельном потоке для избежания


блокирования графического интерфейса.
Отправка сообщения клиентом реализована аналогично тому, как это выполняет
агент. XML-файл с уже известной структурой отправляется по UDP-каналу
непосредственно агенту (см. листинг 10)[9].

Листинг 10 - Обработчик события sendButton_Click

Пример работы системы представлен на рисунке 6.

9
Рисунок 6 - Пример работы программы

Как видно из рисунка, два клиента обмениваются сообщениями, которые


регистрируются агентом. Далее необходимо реализовать проверку и повторную отправку
сообщений с случае, если получатель находится вне сети (шаблон Dead Letter Channel, см.
выше). Для этого целесообразно использовать коллекцию для хранения сообщений,
которые невозможно передать в данный момент, очищая её в случае, если получатель
оказался в сети. Проверку наличия получателя в сети можно проводить с помощью
методов сокета, принадлежащего классу UdpClient, отправляя специальные пакеты на
порт получателя и ожидая подтверждения приёма каждого пакета. На рисунке 7 показана
диаграмма, описывающая весь процесс.

Рисунок 7 - Диаграмма последовательности проверки и повторной отправки сообщений

10
Проверкой порта получателя занимается метод CheckPort. Его реализация показана
на листинге 11.

Листинг 11 - Метод CheckPort

Специальное сообщение отправляется на нужный порт, ожидая ответа на


отдельном порте для прослушивания. Если получатель есть в сети, он на известный порт
отправит ответ определенного формата. Если же ответ не придет, брокер выходит из
состояния ожидания и сохраняет сообщение в специальном канале (свойство
Socket.ReceiveTimeout отвечает за разблокировку брокера). Таким образом требуется
обработать два случая: если получатель брокеру неизвестен и если получатель
неожиданно пропал из сети, и брокер ещё не убрал сведения о порте данного получателя
из своей базы данных. На листинге 12 показана обработка первого случая.

Листинг 12 - Обработка случая “получатель не в сети”

11
Сообщение удаляется из главной очереди сообщений, из базы данных удаляется
информация о порте получателя, после чего проверяется канал Dead Letter Channel. В нём
либо создаётся очередь сообщений для данного получателя, либо сообщение добавляется
в уже существующую очередь. Когда получатель возвращается в сеть, он будет заново
зарегистрирован, и ему будут отправлены все сообщения, которые брокер сохранил за
время отсутствия получателя в сети. На листинге 13 показан код, реализующий данную
функциональность.

Листинг 13 - Обработка события “получатель вернулся в сеть”

Новый порт регистрируется в базе данных брокера, канал сохраненных писем


проверяется на наличие сообщений для получателя, после чего они отправляются на
новый порт в той последовательности, в какой были получены. Канал DLC в конце
операции очищается.
Код проекта доступен по ссылке:
https://gitlab.ati.utm.md/brinzan.leon/git_repo_pad_lab1

12
Вывод
В ходе данной работы была создана система, предаставляющая собой приложение
для чата (клиент) и брокер/агент сообщений, являющийся каналом передачи и хранения
сообщений, полученных от клиентов.
Из нереализованных функциональностей, которые в дальнейшем следует добавить,
можно выделить следующие возможности. Во-первых, возможность принятия сообщения
несколькими клиентами сразу. Это реализуемо с помощью метода broadcast, доступного в
.NET UDP-сокетам. Во-вторых, в данный момент при падении брокера все сообщения,
хранящиеся в его Dead Letter Channel никуда не сохраняются и теряются. Целесообразно
было бы реализовать механизм их хранения на жестком диске с возможностью
восстановления при появлении брокера в сети. Для этого подойдёт шаблон Wiretap.

13
Список литературы
1. Hohpe, G. Messaging Patterns Overview [электронный ресурс] : / Gregor Hohpe,
Bobby Woolf. - Enterprise Integration Patterns - Addison-Wesley [2003, 2015] гг. -
Режим доступа :​ ​http://www.enterpriseintegrationpatterns.com/patterns/messaging/
2. Using UDP Services [электронный ресурс] : / Microsoft corporation - MSDN Library
Documentation , [2015] г. - Режим доступа :
https://msdn.microsoft.com/en-us/library/tst0kwb1(v=vs.110).aspx
3. ConcurrentQueue<T> Class [электронный ресурс] : / Microsoft corporation - MSDN
Library Documentation , [2015] г. - Режим доступа :
https://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx
4. System.Net.Sockets Namespace [электронный ресурс] : / Microsoft corporation -
MSDN Library Documentation , [2015] г. - Режим доступа :
https://msdn.microsoft.com/en-us/library/ms978706.aspx
5. Broker [электронный ресурс] : / Microsoft corporation - MSDN Library Documentation
, [2015] г. - Режим доступа :​ ​https://msdn.microsoft.com/en-us/library/ff648849.aspx
6. How to: Make Thread-Safe Calls to Windows Form Controls [электронный ресурс] : /
Microsoft corporation - MSDN Library Documentation , [2015] г. - Режим доступа :
https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx
7. How to: Manipulate Controls from Threads [электронный ресурс] : / Microsoft
corporation - MSDN Library Documentation , [2015] г. - Режим доступа :
https://msdn.microsoft.com/en-us/library/757y83z4.aspx
8. Control.Invoke Method (Delegate) [электронный ресурс] : / Microsoft corporation -
MSDN Library Documentation , [2015] г. - Режим доступа :
https://msdn.microsoft.com/en-us/library/zyzhdc6b(v=vs.110).aspx
9. Slama, J. Select XML Nodes by Name [электронный ресурс] : / Jan Slama. - Czech
Republic, C# Examples - csharp-examples.net , [2010] г. - Режим доступа :
http://www.csharp-examples.net/xml-nodes-by-name/

14
Приложение
Исходный код
Класс MessageObject
using​ ​System;
using​ ​System​.​Collections​.​Generic;
using​ ​System​.​Linq;
using​ ​System​.​Text;
using​ ​System​.​Threading​.​Tasks;
namespace​ padmolib
{
​[​Serializable]
​public​ ​class​ ​MessageObject
{
​private​ ​String​ _message;
​private​ ​String​ _port;
​private​ ​String​ _from;
​private​ ​String​ _to;
​public​ ​String​ ​Message
{
​get​ ​{​ ​return​ _message​;​ }
​set​ ​{​ _message ​=​ value​;​ }
}
​public​ ​String​ ​Port
{
​get​ ​{​ ​return​ _port​;​ }
​set​ ​{​ _port ​=​ value​;​ }
}
​public​ ​String​ ​From
{
​get​ ​{​ ​return​ _from​;​ }
​set​ ​{​ _from ​=​ value​;​ }
}
​public​ ​String​ ​To
{
​get​ ​{​ ​return​ _to​;​ }
​set​ ​{​ _to ​=​ value​;​ }
}
}
}

Клиент
using​ ​System;
using​ ​System​.​Collections​.​Generic;
using​ ​System​.​Collections​.​Concurrent;
using​ ​System​.​ComponentModel;
using​ ​System​.​Data;

15
using​ ​System​.​Drawing;
using​ ​System​.​Linq;
using​ ​System​.​Text;
using​ ​System​.​Threading​.​Tasks;
using​ ​System​.​Windows​.​Forms;
using​ ​System​.​IO;
using​ ​System​.​Net;
using​ ​System​.​Net​.​Sockets;
using​ ​System​.​Diagnostics;
using​ ​System​.​Xml;

using​ padmolib;

namespace​ PAD_1
{
​public​ ​partial​ ​class​ ​Form1​ ​:​ ​Form
{

​static​ ​int​ listenPort;


​static​ ​ConcurrentQueue​<​MessageObject​>​ messageQueue;
​delegate​ ​void​ ​ShowReceivedMessageDelegate​();
​MessageObject​ mo;

​public​ ​Form1​()
{
messageQueue ​=​ ​new​ ​ConcurrentQueue​<​MessageObject​>();

listenPort ​=​ ​AssignPort​();

​Task​ t ​=​ ​new​ ​Task​(()​ ​=>​ ​StartListenerAsync​());


t​.​Start​();

​InitializeComponent​();

​ rivate​ ​void​ ​Form1_Load​(​object​ sender​,​ ​EventArgs​ e)


p
{

​ rivate​ ​void​ sendButton_Click​(​object​ sender​,​ ​EventArgs​ e)


p
{
​Debug​.​WriteLine​(​"Send event detected."​);

​if​ ​(​sentTextBox​.​Text​ ​!=​ ​"")


{
​String​ textMessage ​=​ sentTextBox​.​Text;
16
receivedTextBox​.​Text ​+= ​String​.​Format​(​"\n[{0}]: {1}"​, ​DateTime​.​Now​,
textMessage​);
sentTextBox​.​Text​ ​=​ ​"";

​ tring
S sendMessage ​=
String​.​Format​(​"<Data>\n\t<To>{0}</To>\n\t<From>{1}</From>\n\t<Port>{2}</Port>\n\t<Mes
sage>{3}</Message>\n</Data>",
​ ext​,​ nameBox​.​Text​,​ listenPort​,​ textMessage​);
friendBox​.T

​Socket​ s ​=​ ​new​ ​Socket​(​AddressFamily​.​InterNetwork​,


​SocketType​.​Dgram,
​ProtocolType​.​Udp​);
​IPAddress​ broadcast ​=​ ​IPAddress​.​Parse​(​"192.168.1.255"​);
​byte​[]​ sendbuf ​=​ ​Encoding​.​ASCII​.​GetBytes​(​sendMessage​);
​IPEndPoint​ ep ​=​ ​new​ ​IPEndPoint​(​broadcast​,​ ​5000​);
s​.​SendTo​(​sendbuf​,​ ep​);
}

​ rivate​ ​void​ ​StartListenerAsync​()


p
{
​Debug​.​WriteLine​(​"Listener executing."​);

​bool​ ​done​ ​=​ ​false;


mo ​=​ ​new​ ​MessageObject​();
​UdpClient​ listener ​=​ ​new​ ​UdpClient​(​listenPort​);
​IPEndPoint​ groupEP ​=​ ​new​ ​IPEndPoint​(​IPAddress​.​Any​,​ listenPort​);
​try
{
​while​ ​(!​done)
{
​byte​[]​ bytes ​=​ listener​.​Receive​(​ref​ groupEP​);
​String receivedMessage ​= ​Encoding​.​ASCII​.​GetString​(​bytes​, ​0​,
bytes​.​Length​);

​XmlDocument​ xmldoc ​=​ ​new​ ​XmlDocument​();


xmldoc​.​LoadXml​(​receivedMessage​);
​XmlNode​ xmln ​=​ xmldoc​.​SelectSingleNode​(​"/Data"​);
mo​.​To​ =
​ ​ xmln​[​"To"​].​InnerText;
mo​.​From​ ​=​ xmln​[​"From"​].​InnerText;
mo​.​Port​ ​=​ xmln​[​"Port"​].​InnerText;
mo​.​Message​ ​=​ xmln​[​"Message"​].​InnerText;

​if​ ​(​mo​.​Message​ ​==​ ​"pingback")


{
​Pingback​();
}
17
​else
{
​ShowReceivedMessageDelegate callback ​= ​new
ShowReceivedMessageDelegate​(​ShowReceivedMessage​);
receivedTextBox​.​Invoke​(​callback​);
}
}

}
​catch​ ​(​Exception​ ex)
{
​Console​.​WriteLine​(e
​ x​.​ToString​());
}
​finally
{
listener​.​Close​();
}
}

​private​ ​void​ ​ShowReceivedMessage​()


{
​Debug​.​WriteLine​(​"ShowMessage executing."​);

receivedTextBox​.​Text​ ​+=​ ​String​.​Format​(​"\n[{0}]: ({1}) {2}",


​DateTime​.​Now,
mo​.​From,
mo​.​Message​);

​private​ ​int​ ​AssignPort​()


{
​Random​ rng ​=​ ​new​ ​Random​();
​int​ port ​=​ rng​.​Next​(​2000​,​ ​5000​);
​return​ port;
}

​private​ ​void​ ​Register​()


{
​Debug​.​WriteLine​(​"Registering with the Broker."​);

​String​ textMessage ​=​ ​"Registering...";


​String sendMessage ​=
String​.​Format​(​"<Data>\n\t<To>{0}</To>\n\t<From>{1}</From>\n\t<Port>{2}</Port>\n\t<Mes
sage>{3}</Message>\n</Data>",
​""​,​ nameBox​.​Text​,​ listenPort​,​ textMessage​);

18
​Socket​ s ​=​ ​new​ ​Socket​(A
​ ddressFamily​.​InterNetwork,
​SocketType​.​Dgram,
​ProtocolType​.​Udp​);

​IPAddress​ broadcast ​=​ I ​ PAddress​.​Parse​(​"192.168.1.255"​);


​byte​[]​ sendbuf ​=​ ​Encoding​.​ASCII​.​GetBytes​(​sendMessage​);
​IPEndPoint​ ep ​=​ ​new​ ​IPEndPoint​(​broadcast​,​ ​5000​);

s​.​SendTo​(​sendbuf​,​ ep​);
}

​private​ ​void​ ​Pingback​()


{
​Debug​.​WriteLine​(​"Pinging broker."​);

​String​ textMessage ​=​ ​"pingback_received";


​String sendMessage ​=
String​.​Format​(​"<Data>\n\t<To>{0}</To>\n\t<From>{1}</From>\n\t<Port>{2}</Port>\n\t<Mes
sage>{3}</Message>\n</Data>",
​""​,​ nameBox​.​Text​,​ listenPort​,​ textMessage​);

​Socket​ s ​=​ ​new​ ​Socket​(A ​ ddressFamily​.​InterNetwork,


​SocketType​.​Dgram,
​ProtocolType​.​Udp​);
​IPAddress​ broadcast ​=​ I ​ PAddress​.​Parse​(​"192.168.1.255"​);
​byte​[]​ sendbuf ​=​ ​Encoding​.​ASCII​.​GetBytes​(​sendMessage​);
​IPEndPoint​ ep ​=​ ​new​ ​IPEndPoint​(​broadcast​,​ ​5001​);

s​.​SendTo​(​sendbuf​,​ ep​);
}

​private​ ​void​ loginButton_Click​(​object​ sender​,​ ​EventArgs​ e)


{
​if​ ​(​nameBox​.​Text​ ​!=​ ​"")
{
loginButton​.​Enabled​ ​=​ ​false;
sendButton​.​Enabled​ ​=​ ​true;
receivedTextBox​.​Text​ ​=​ ​"Welcome, "​ ​+​ nameBox​.​Text​ ​+​ ​"!";

​Task​ r ​=​ ​new​ ​Task​(()​ ​=>​ ​Register​());


r​.​Start​();
}
}
}
}

Агент

19
using​ ​System;
using​ ​System​.​Collections​.​Generic;
using​ ​System​.​Collections​.​Concurrent;
using​ ​System​.​Linq;
using​ ​System​.​Text;
using​ ​System​.​Threading​.​Tasks;
using​ ​System​.​IO;
using​ ​System​.​Net;
using​ ​System​.​Net​.​Sockets;
using​ ​System​.​Xml;
using​ padmolib;
using​ ​System​.​Net​.​NetworkInformation;

namespace​ ​Broker
{

​class​ ​Program
{
​static​ ​int​ listenPort ​=​ ​5000;
​static​ ​int​ destinationPort;
​static​ ​ConcurrentQueue​<​MessageObject​>​ messageQueue;
​static​ ​Dictionary​<S​ tring​,​ S​ tring​>​ db;
​static​ ​Dictionary​<S​ tring​,​ L​ ist​<​MessageObject​>>​ dlc​;/
​ /Dead Letter Channel

​private​ ​static​ ​void​ ​StartListener​()


{
​bool​ ​done​ ​=​ ​false;
​MessageObject​ item;
​String​ val;
​String​ destinationPort;
​List​<​MessageObject​>​ valList;

db ​=​ ​new​ ​Dictionary​<​String​,​ ​String​>();


messageQueue ​=​ ​new​ ​ConcurrentQueue​<​MessageObject​>();
dlc ​=​ ​new​ ​Dictionary​<​String​,​ ​List​<​MessageObject​>>();

​UdpClient​ listener ​=​ ​new​ ​UdpClient​(​listenPort​);


​IPEndPoint​ groupEP ​=​ ​new​ ​IPEndPoint​(​IPAddress​.​Any​,​ listenPort​);
​try
{
​while​ ​(!​done)
{
​Console​.​WriteLine​(​"Waiting for message"​);
​byte​[]​ bytes ​=​ listener​.​Receive​(​ref​ groupEP​);
​String receivedMessage ​= ​Encoding​.​ASCII​.​GetString​(​bytes​, ​0​,
bytes​.​Length​);
​Console​.​WriteLine​(​"Received message from {0}:\n{1}",
groupEP​.​ToString​(),
20
receivedMessage​);

​MessageObject​ mo ​=​ ​new​ ​MessageObject​();

​XmlDocument​ xmldoc ​=​ ​new​ ​XmlDocument​();


xmldoc​.​LoadXml​(​receivedMessage​);
​XmlNode​ xmln ​=​ xmldoc​.​SelectSingleNode​(​"/Data"​);
mo​.​To​ =
​ ​ xmln​[​"To"​].​InnerText;
mo​.​From​ ​=​ xmln​[​"From"​].​InnerText;
mo​.​Port​ ​=​ xmln​[​"Port"​].​InnerText;
mo​.​Message​ ​=​ xmln​[​"Message"​].​InnerText;

​if​ ​(!​db​.​ContainsKey​(​mo​.​From​))
{
db​.​Add​(​mo​.F
​ rom​,​ mo​.​Port​);
}
​if​ ​(​mo​.​Message​ ​!=​ ​"Registering...")
{
messageQueue​.​Enqueue​(​mo​);

​if​ ​(​db​.​TryGetValue​(​mo​.​To​,​ ​out​ val​))


{
destinationPort ​=​ val;
​//if
(ShowActiveUdpListeners(Int32.Parse(destinationPort)))
​if​ ​(​CheckPort​(​Int32​.​Parse​(​destinationPort​)))
{
​if​ ​(​messageQueue​.​TryDequeue​(o
​ ut​ item​))
{
​SendMessage​(​item​.​Message​,
Int32​.​Parse​(​destinationPort​),​ item​.​To​,​ item​.​From​);
}
}
​else
{
​Console​.​WriteLine​(​"Recepient offline."​);

​if​ ​(​messageQueue​.​TryDequeue​(o ​ ut​ item​))


{
​Console​.​WriteLine​(​"Removing message from the
message queue: "​ ​+​ item​.​Message​);
db​.​Remove​(​item​.​To​);
​if​ ​(!​dlc​.​ContainsKey​(​item​.​To​))
{
​List​<​MessageObject​> l ​= n
​ ew
List​<​MessageObject​>();
l​.​Add​(​item​);
dlc​.​Add​(​item​.​To​,​ l​);
21
}
​else
​{
dlc​[​item​.​To​].​Add​(​item​);
}
}
}
}
​else
{
​Console​.​WriteLine​(​"Recepient unregistered."​);
​if​ ​(​messageQueue​.​TryDequeue​(​out​ item​))
{
​Console​.​WriteLine​(​"Removing message from the message
queue: "​ ​+​ item​.​Message​);
​if​ ​(!​dlc​.​ContainsKey​(​item​.​To​))
{
​List​<​MessageObject​> l ​= ​new
List​<​MessageObject​>();
l​.​Add​(​item​);
dlc​.​Add​(​item​.​To​,​ l​);
}
​else
{
dlc​[​item​.​To​].​Add​(​item​);
}
}
}
}
​else
{
​int​ newPort ​=​ ​Int32​.​Parse​(​mo​.​Port​);
​Console​.​WriteLine​(​"Unloading messages at {0}\n."​,​ newPort​);

​if​ ​(​dlc​.​TryGetValue​(​mo​.​From​,​ ​out​ valList​))


{
​foreach​ ​(​MessageObject​ m ​in​ valList)
{
​Console​.​WriteLine​(​"Sending '{0}' to {1}"​, m​.​Message​,
m​.​To​);
​SendMessage​(​m​.​Message​,​ newPort​,​ m​.​To​,​ m​.​From​);
}
dlc​.​Clear​();
}
}
}
}

22
​catch​ ​(​Exception​ e)
{
​Console​.​WriteLine​(e ​ ​.​ToString​());
}
​finally
{
​Console​.​WriteLine​(" ​ Something went wrong, listener closing..."​);
​Console​.​ReadKey​();
listener​.​Close​();
}
}

​ ublic ​static ​void ​SendMessage​(​String message​, i


p ​ nt destination​, ​String to​,
String​ ​from​)
{
​String sendMessage ​=
String​.​Format​(​"<Data>\n\t<To>{0}</To>\n\t<From>{1}</From>\n\t<Port>{2}</Port>\n\t<Mes
sage>{3}</Message>\n</Data>",
to​,​ ​from​,​ listenPort​,​ message​);

​Socket​ s ​=​ ​new​ ​Socket​(A


​ ddressFamily​.​InterNetwork,
​SocketType​.​Dgram,
​ProtocolType​.​Udp​);

​IPAddress​ broadcast ​=​ I


​ PAddress​.​Parse​(​"192.168.1.255"​);

​byte​[]​ sendbuf ​=​ ​Encoding​.​ASCII​.​GetBytes​(​sendMessage​);


​IPEndPoint​ ep ​=​ ​new​ ​IPEndPoint​(​broadcast​,​ destination​);

​Console​.​WriteLine​(" ​ Sending message to {0}: {1}",


ep​.​ToString​(),
sendMessage​);
s​.​SendTo​(​sendbuf​,​ ep​);
}

​public​ ​static​ b
​ ool​ ​ShowActiveUdpListeners​(​int​ destination)
{
​bool​ checkResult ​=​ ​false;

​IPGlobalProperties properties ​=
IPGlobalProperties​.​GetIPGlobalProperties​();
​IPEndPoint​[]​ endPoints ​=​ properties​.​GetActiveUdpListeners​();

​foreach​ ​(​IPEndPoint​ e ​in​ endPoints)


{
​if​ ​(​e​.​Port​ ​==​ destination)
{

23
checkResult ​=​ t
​ rue;
}
}

​return​ checkResult;
}

​public​ ​static​ ​bool​ ​CheckPort​(​int​ destination)


{
​SendMessage​(​"pingback"​,​ destination​,​ ​""​,​ ​""​);
​bool​ result ​=​ f ​ alse;
​bool​ ​done​ = ​ ​ ​false;
​MessageObject​ mo ​=​ ​new​ ​MessageObject​();
​UdpClient​ listener = ​ ​ ​new​ ​UdpClient​(​5001​);
listener​.​Client​.​ReceiveTimeout​ ​=​ ​500;
​IPEndPoint​ groupEP ​=​ ​new​ ​IPEndPoint​(​IPAddress​.​Any​,​ ​5001​);
​try
{
​while​ ( ​ !​done)
{
​#region LISTENING FOR MESSAGES
​byte​[]​ bytes ​=​ listener​.​Receive​(​ref​ groupEP​);
​String receivedMessage ​= ​Encoding​.​ASCII​.​GetString​(​bytes​, ​0​,
bytes​.​Length​);

​XmlDocument​ xmldoc ​=​ ​new​ ​XmlDocument​();


xmldoc​.​LoadXml​(​receivedMessage​);
​XmlNode​ xmln ​=​ xmldoc​.​SelectSingleNode​(​"/Data"​);
mo​.​To​ =​ ​ xmln​[​"To"​].​InnerText;
mo​.​From​ ​=​ xmln​[​"From"​].​InnerText;
mo​.​Port​ ​=​ xmln​[​"Port"​].​InnerText;
mo​.​Message​ ​=​ xmln​[​"Message"​].​InnerText;
​#endregion
​if​ ​(​mo​.​Message​ ​==​ ​"pingback_received")
{
​done​ ​=​ ​true;
result ​=​ ​true;
}
​else
{
​done​ ​=​ ​true;
}
}

}
​catch​ ​(​Exception​ ex)
{
​Console​.​WriteLine​(​ex​.​ToString​());
24
}
​finally
{
listener​.​Close​();
}

​return​ result;
}

​ tatic​ ​void​ ​Main​(​string​[]​ args)


s
{
​StartListener​();
}
}
}

25