Главная страница
Навигация по странице:

  • 3.2.3 Реализация проекта программных средств

  • 3.3 Разработка панели управления для сервера обработки сообщений

  • 4 Тестирование 4.1 Выбор методологии тестирования

  • 4.2 Компонентное тестирование

  • Список использованных источников

  • курсовая приближенно по серверам. Удаленные программные средства первичной


    Скачать 2.16 Mb.
    НазваниеУдаленные программные средства первичной
    Анкоркурсовая приближенно по серверам
    Дата29.04.2023
    Размер2.16 Mb.
    Формат файлаpdf
    Имя файлакурсовая приближенно по серверам.pdf
    ТипДокументы
    #1096856
    страница4 из 4
    1   2   3   4
    3.2.2 Разработка API для прикладных систем
    Вторым этапом разработки данного программного средства является разработка программного интерфейса (API) для подключения компонентов прикладных систем. Данный интерфейс разрабатывается в виде легковесной библиотеки на языке C#, которую можно подключать из внешних приложений. Важно, что все публичные методы этого API должны быть задокументированы для удобства интеграции.
    В задачи разрабатываемого интерфейса входит обеспечение обмена информацией между компонентом и сервером обработки сообщений. Все методы должны быть синхронными для лучшего встраивания в уже существующую структуру приложения компонента – обеспечение асинхронности действий ложится на плечи разработчиков самого компонента. Разрабатываемый интерфейс должен быть достаточно низкоуровневым для гибкой работы с ним– но не должен перегружать программиста компонента внутренней логикой работы. Интерфейс был разработан в соответствии с требованиями.
    Программный интерфейс состоит из одного подключаемого класса
    EvCommunication, который управляет соединением с сервером. Настройка соединения происходит в конструкторе класса с помощью параметров, переданных конструктору. При режиме pull конструктору класса передаются

    50 адрес сервера обработки сообщений и кодовое имя компонента системы. При режиме push – номер TCP-порта компонента прикладной системы и кодовое имя компонента системы. Соединение производится методом Open, который вызывается конструктором класса. Метод EncryptConnection отвечает за инициализацию шифрования соединения. Данный метод в качестве аргумента принимает IP-адрес и TCP-порт сервера обработки сообщения в формате строки. Отправка сообщения реализуется в методе SendMessage.
    Аргументом данного метода является строка сообщения. Все свойства класса
    EvCommunication описаны в таблице 14.
    Таблица 14 – Свойства класса EvCommunication
    Название свойства
    Тип свойства
    Описание свойства
    _sr
    StreamReader
    Читает символы из потока
    _sw
    StreamWriter
    Записывает символы в поток
    _stream
    Stream
    Хранит поток символов
    _client
    TcpClient
    Служит для установления соединения в режиме pull
    _listener
    TcpListener
    Служит для установления соединения в режиме push
    _client_mode bool
    Определяет режим обмена сообщениями (true - режим pull, false - режим push)
    Для описания взаимодействия API с другими программными средствами была построена диаграмма последовательности в нотации UML.
    Диаграмма последовательности работы с API изображена на рисунке
    16. Данная диаграмма покрывает все варианты использования API: от создания и опционального шифрования соединения до закрытия соединения.
    Закрытие соединения также опционально и не должно проводиться после отправки каждого сообщения; при завершении родительского процесса соединение завершится автоматически.

    51
    Рисунок 16 – Диаграмма последовательности API
    Принимаемое сообщение сериализуется самим компонентом, так как встроенная библиотека сериализации в формат JSON может не поддерживать всех опций, необходимых для конкретного объекта – и в таком случае разработчик компонента будет вынужден отказаться от использования системы.
    3.2.3 Реализация проекта программных средств
    После разработки архитектуры программное средство сервера приема и обработки сообщений было закодировано с использованием языка C# в среде разработки MonoDevelop 5.9. Программное средство было реализовано в виде подключаемой библиотеки для разделения реализации пользовательского интерфейса и самого сервера. На рисунке 17 приведена диаграмма компонентов программного средства.

    52
    Рассмотрим диаграмму подробнее. Так как данная часть реализации содержит только логическую часть, то весь код программного средства содержится в файлах исходного кода языка C# с расширением «.cs». Для соблюдения чистоты кода каждый класс программного средства за исключением прокси-классов, сгенерированных утилитой Cache Managed
    Provider for .NET был выделен в отдельный файл исходного кода.
    Рисунок 17 – Диаграмма компонентов ПС сервера приема и обработки сообщений
    Компоненты программного средства включают в себя:

    EvServerSystem.dll – подключаемую библиотеку данного ПС;

    Base.cs – файл с модулем-интерфейсом к системе акторов;

    Server.cs – файл класса корневого актора Server;

    SettingsStorage.cs
    – файл класса хранилища настроек
    SettingsStorage;

    ServerSocketActor.cs – файлкласса ServerSocketActor;

    ServerSocket.cs – файлкласса ServerSocket;

    EventClass.cs – файлпрокси-классов Cache Managed Provider for
    .NET, содержащийклассы Event, EventContext икласс KeyValuePair;

    DBConnection.cs – файлклассасоединениясБД DBConnection;

    53

    AppSocketActor.cs – файлкласса AppSocketActor;

    AppSocket.cs – файлкласса AppSocket.
    3.3 Разработка панели управления для сервера обработки
    сообщений
    Для удобного управления сервером обработки сообщений требуется разработать панель управления.
    Панель управления позволяет пользователю (администратору ПС) управлять состоянием сервера обработки сообщений, просматривать и изменять конфигурацию соединения с БД и компонентами ПС, а также отслеживать статистику работы сервера.
    Пользовательский интерфейс панели управления состоит из 4-х вкладок:

    «Сервер» – содержит элементы для управления состоянием сервера (рисунок 18);

    «Компоненты» – позволяет управлять соединениями с компонентами ПС (рисунок 19);

    «Соединение» – включает в себя элементы управления конфигурацией соединения с сервером БД журнала событий (рисунок 20);

    «Статистика» – отображает статистику работы сервера обработки сообщений (рисунок 21).

    54
    Рисунок 18 – Вкладка «Сервер» панели управления сервером обработки сообщений
    Рисунок 19 – Вкладка «Компоненты» панели управления сервером обработки сообщений

    55
    Рисунок 20 – Вкладка «Соединение» панели управления сервером обработки сообщений
    Рисунок 21 – Вкладка «Статистика» панели управления сервером обработки сообщений
    В нижней части диалогового окна панели управления расположены кнопка для сброса настроек сервера обработки сообщений к заводской конфигурации и кнопка применения внесенных изменений. Обработчики нажатия данных кнопок после успешного исполнения своих инструкций

    56 создают диалоговое окно, информирующее пользователя о необходимости перезагрузки сервера обработки сообщений для применения изменений.

    57
    4 Тестирование
    4.1 Выбор методологии тестирования
    Тестирование программного обеспечения – процесс выявления ошибок в программном обеспечении (ПО). Существующие на сегодняшний день методы тестирования ПО не позволяют однозначно и полностью устранить все дефекты и ошибки и установить корректность функционирования анализируемой программы особенно в закрытых частных программах.
    Поэтому все существующие методы тестирования действуют в рамках формального процесса проверки исследуемого или разрабатываемого ПО.
    Такой процесс формальной проверки или верификации может доказать, что дефекты отсутствуют, с точки зрения используемого метода – то есть нет никакой возможности точно установить или гарантировать отсутствие дефектов в программном продукте с учетом человеческого фактора, присутствующего на всех этапах жизненного цикла ПО.
    Существует множество подходов к решению задачи тестирования и верификации ПО, но эффективное тестирование сложных программных продуктов – это процесс в высшей степени творческий, не сводящийся к следованию строгим и четким процедурам или созданию таковых.
    Одними из таких подходов к тестированию являются методологии компонентного/модульного тестирования и методология функционального тестирования. Для тестирования разработанных программных средств было принято решение использовать методологии компонентного и функционального тестирования.
    Компонентное
    (или модульное) тестирование проверяет функциональность и ищет дефекты в частях приложения, которые доступны и могут быть протестированы по-отдельности (модули программ, объекты, классы, функции и т.д.). Обычно компонентное (модульное) тестирование

    58 проводится с помощью вызова кода, который необходимо проверить и при поддержке сред разработки, таких как фреймворки для модульного тестирования или инструменты для отладки.
    Один из наиболее эффективных подходов к компонентному
    (модульному) тестированию – это подготовка автоматизированных тестов до начала основного кодирования (разработки) программного обеспечения. Это называется разработка от тестирования (test-driven development) или подход тестирования вначале (test first approach). При этом подходе создаются и интегрируются небольшие куски кода, напротив которых запускаются тесты, написанные до начала кодирования. Разработка ведется до тех пор пока все тесты не будут успешно пройдены.
    Несмотря на свою схожесть, компонентное и модульное тестирование не являются синонимами [13]. Хотя в общем данные методики тестирования представляют одно и тоже, разница лишь в том, что в компонентном тестировании в качестве параметров функций используют реальные объекты и драйверы, а в модульном тестировании – конкретные значения.
    Функциональное тестирование является еще одним из ключевых видов тестирования, задача которого – установить соответствие разработанного программного обеспечения исходным функциональным требованиям заказчика. Проведение функционального тестирования позволяет проверить способность информационной системы в определенных условиях решать задачи, нужные пользователям.
    В зависимости от степени доступа к коду системы можно выделить два типа функциональных испытаний:
    ー тестирование black box (черный ящик) – проведение функционального тестирования без доступа к коду системы;
    ー тестирование white box (белый ящик) – функциональное тестирование с доступом к коду системы.

    59
    Тестирование black box проводится без знания внутренних механизмов работы системы и опирается на внешние проявления ее работы. При этом тестировании проверяется поведение ПО при различных входных данных и внутреннем состоянии систем. В случае тестирования white box создаются тест-кейсы, основанные преимущественно на коде системы ПО. Также существует расширенный тип black-box тестирования, включающего в себя изучение кода, – так называемый grey box (серый ящик).
    4.2 Компонентное тестирование
    Методология компонентного тестирования заключается в написании юнит-тестов (кода, моделирующего различные варианты использования программного средства), их запуска и просмотра результатов тестирования.
    В данной работе используется фреймворк тестирования NUnit, облегчающий написание и запуск тестов, который также позволяет проводить тестирование на ОС Linux в среде разработки MonoDevelop (в отличие от Microsoft Testing
    Framework, работающего только в среде разработки Visual Studio).
    NUnit – открытая среда юнит-тестирования приложений для приложений на платформе .NET. Она была портирована с языка Java, реализация на котором известна, как JUnit. Первые версии NUnit были написаны на J#, но затем весь код был переписан на C# с использованием таких новшеств .NET, как атрибуты [14].
    Существуют также известные расширения оригинального пакета NUnit, большая часть из них также с открытым исходным кодом. NUnit.Forms дополняет NUnit средствами тестирования элементов пользовательского интерфейса Windows Forms. NUnit.ASP выполняет ту же задачу для элементов интерфейса в ASP.NET.
    Тесты NUnit могут выполняться как с помощью отдельного менеджера тестов NUnit, так и с помощью IDE MonoDevelop или встраиваться в проекты

    60
    Microsoft Test Project, позволяя использовать менеджер тестов среды разработки Visual Studio.
    В данной работе для тестирования использовался менеджер тестов среды разработки MonoDevelop.
    В процессе разработки и кодирования программных средств были выделены два основных компонента системы: API и сервер приема и обработки, каждый из которых был разделен на отдельные модули. Для основных модулей были написаны модульные тесты (код тестов находится в приложении А).
    Для улучшения кода юнит-тестов пришлось видоизменить класс
    SettingsStorage путем добавления к нему двух методов для инициализации настроек из памяти.
    Тесты были написаны для основных процессов: загрузка настроек, соединение, проверка протокола и передача сообщений. Результаты выполнения тестов изображены на рисунке 22.
    Рисунок 22 – Результаты работы модульных тестов
    Как видно, все тесты были отработаны успешно.
    Попутно с тестированием были рассчитаны метрики кода. В рассчитанные метрики входят цикломатическая сложность программы, глубина наследования и сопровождаемость.

    61
    Цикломатическая сложность программы (ЦСП, англ. Cyclomatic complexity) – структурная (или топологическая) мера сложности программ, используемая для измерения качества программного обеспечения, основанная на методах статического анализа кода. ЦСП равна увеличенному на единицу цикломатическому числу графа программы. При вычислении цикломатической сложности используется граф потока управления программы: узлы графа соответствуют неделимым группам команд программы и ориентированным ребрам, каждый из которых соединяет два узла и соответствует двум командам, вторая из которых может быть выполнена сразу после первой. Цикломатическая сложность может также быть применена для отдельных функций, модулей, методов или классов в пределах программы.
    Цикломатическая сложность части программного кода — количество линейно независимых маршрутов через программный код. Например, если исходный код не содержит никаких точек ветвления или циклов, то сложность равна единице, поскольку, есть только единственный маршрут через код. Если код имеет единственный оператор «if», содержащий простое условие, то существует два пути через код: один если условие оператора «if» принимает значение «true» и второй, когда условие принимает значение
    «false».
    Индекс удобства поддержки (сопровождаемость) же вычисляется на основе трех метрик: цикломатическая сложность, число строк кода и вычислительная сложность. Значение индекса ниже 10 обозначает проблемный код, от 10 до 20 – код, модификация которого сложна.
    Цикломатическая сложность ПС сервера приема и обработки сообщений имеет значение 294, из которых 171 путь вносят сгенерированные прокси-классы базы данных. Индекс сопровождаемости всего проекта имеет значение 75 (хорошо), а средняя глубина наследования классов – 4.
    Результаты функционального тестирования отображены в таблице 15.

    62
    Таблица 15 – Результаты функционального тестирования
    Загрузка настроек
    Запустить систему акторов и дождаться загрузки настроек
    Соответствует ожиданиям
    Запуск сервера
    Запустить систему акторов и дождаться сообщение о готовности принимать соединения
    Соответствует ожиданиям
    Соединение с сервером
    Установить соединение с сервером с помощью API
    Соответствует ожиданиям
    Установка защищенного соединения
    Отправить запрос на защиту соединения
    Соединение зашифровано, соответствует ожиданиям
    Отправка сообщения
    Отправить сообщение от прикладной системы
    Сообщение сохранено в БД, соответствует ожиданиям
    Отключение от сервера
    Отправить запрос на закрытие соединения
    Соединение разорвано, соответствует ожиданиям
    В ходе проведения функционального тестирования было выявлено, что программные средства исправно выполняют свои функции.

    63
    Заключение
    В процессе выполнения выпускной квалификационной работы был выполнен анализ предметной области, в рамках которого были проанализированы схожие существующие программные средства приема и анализа сообщений, были оценены их подходы, поставлена задача на разработку и определена стоимость разработки. Был проведен анализ требований, заключающийся в проведении анализа вариантов использования программных средств и уточнении требований. Были определены подходы к различным действиям и общая архитектура программных средств; были определены средства разработки ПС.
    В результате были разработаны программные средства доставки, приема и обработки сообщений, а именно API для компонентов прикладной системы и сервер обработки сообщений.
    Разработанные программные средства выполняют все поставленные задачи и соответствуют требованиям, поставленным к работе.
    Тестирование заключалось в определении методов тестирования и выполнении тестов. Были написан код модульных тестов, с помощью которого выполнялось тестирование ПС. Результаты компонентного и функционального тестирования показали корректность работы программных средств.

    64
    Список использованных источников
    1.
    The Original StatsD [Электронный ресурс]. – Режим доступа: https://github.com/iamcal/Flickr-StatsD – (проверено 25.02.2016).
    2.
    StatsD Metrics [Электронный ресурс]. – Режим доступа: https://github.com/b/statsd_spec – (проверено 25.02.2016).
    3.
    Мониторинг сервисов с Prometheus [Электронный ресурс]. –
    Режим доступа: https://habrahabr.ru/company/selectel/blog/275803/

    (проверено 25.02.2016).
    4.
    Ganglia и Nagios. Взаимодополняющий удаленный мониторинг
    [Электронный ресурс]. – Режим доступа: https://habrahabr.ru/post/166171/ –
    (проверено 25.02.2016).
    5.
    Ganglia Monitoring For Sysadmins [Электронный ресурс]. – Режим доступа: https://github.com/ganglia/monitor-core/wiki/Ganglia-Documents
    (проверено 25.02.2016).
    6. Биография Генри Ганта [Электронный ресурс]. – Режим доступа: http://www.mental-skills.ru/synopses/524.html – (проверено 09.06.2016).
    7.
    Иванов Ф. Н. Д. Моделирование на UML [Электронный ресурс]. – Режим доступа: http://book.uml3.ru/sec_1_7 – (проверено
    27.04.2016).
    8. Общие сведения о платформе .NET Framework [Электронный ресурс].

    Режим доступа: https://msdn.microsoft.com/ru- ru/library/zw4w595w(v=vs.110).aspx – (проверено 09.06.2016).
    9.
    Brazil B. Push vs Pull for Monitoring [Электронный ресурс]. –
    Режим доступа: http://www.boxever.com/push-vs-pull-for-monitoring

    (проверено 25.02.2016).
    10.
    E
    l-Kady S. Poodle Hastened the Death of SSL v3.0 [Электронный ресурс]. – Режим доступа: http://www.digitalqatar.qa/en/2014/11/13/poodle- hastened-the-death-of-ssl-v3-0/ – (проверено 29.04.2016).

    65 11.
    Таненбаум Э. Компьютерные сети. — Питер; 2007. — 992 c
    12.
    Caché | Современная система управления базами данных |
    InterSystems
    [Электронный ресурс].

    Режим доступа: http://www.intersystems.com/ru/our-products/cache/cache-overview/

    (проверено 21.05.2016).
    13.
    Про Тестинг - Тестирование - Уровни Тестирования ПО -
    Компонентное или Модульное тестирование - Component or Unit Testing
    [Электронный ресурс].

    Режим доступа: http://www.protesting.ru/testing/levels/component.html

    (проверено
    21.05.2016).
    14.
    Thomas A. H. D. Pragmatic Unit Testing in C# with NUnit. — The
    Pragmatic Bookshelf; 2004. — 159 c.

    66
    ГЛОССАРИЙ
    Приложение А
    (обязательное)

    67
    Масштабирование – наращивание вычислительной мощности системы.
    Масштабируемость – способность системы, сети или процесса справляться с увеличением рабочей нагрузки (увеличивать свою производительность) при масштабировании.
    Кластер – объединение нескольких однородных элементов, которое может рассматриваться как самостоятельная единица, обладающая определенны-ми свойствами. В информатике – группа компьютеров, объединенных высокоскоростными каналами связи и представляющая с точки зрения пользователя единый аппаратный ресурс.
    Журнализация – процесс записи в системный журнал информации о сообщениях, запросах, выполнявшихся программах, использованных наборах данных и других сведений.
    Мониторинг – процесс непрерывного наблюдения и регистрации данных о каком-либо объекте или наблюдение за состоянием процесса.
    Метрика – значение определенного параметра какого-либо процесса.
    API
    – англ. application programming interface, интерфейс программирования приложений.
    Язык UML – общецелевой язык визуального моделирования, который разработан для спецификации, визуализации, проектирования и документирования компонентов программного обеспечения, бизнес- процессов и других систем.
    Система
    - это множество элементов(подсистем, объектов), находящихся в отношениях и связях друг с другом. Совокупность подсистем, составляющих систему, служит определенной цели. Каждый объект системы имеет свое состояние и свой набор свойств и может являться системой меньшего размера.
    Подсистема - набор объектов и подсистем, обеспечивающих некоторую функциональность и взаимодействующих как между собой, так и с внешней

    68 системой (через определенный интерфейс). В состав подсистемы может входить один или более взаимонезависимых объектов и/или подсистем.
    Сообщение - форма представления информации, имеющая признаки начала и конца и предназначенная для передачи по какой-либо среде связи. В данной работе каждое сообщение содержит либо уведомление о происхождении какого-либо события, либо информацию о каком-либо свойстве подсистемы.
    Отказоустойчивость – свойство технической системы сохранять свою работоспособность после отказа одного или нескольких составных компонентов.
    Фреймворк – англ. framework, каркас – программная платформа, определяющая структуру программной системы; программное обеспечение, облегчающее разработку и объединение разных компонентов большого программного проекта.

    69
    ЛИСТИНГ ПРОГРАММЫ
    Приложение Б
    (обязательное)

    70
    Base.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Akka.Actor; using Akka.Monitoring; using Akka.Monitoring.StatsD; namespace EvServerSystem
    { public class Base
    {
    Akka.Actor.ActorSystem _system;
    IActorRef Core; public void Init ()
    {
    Console.WriteLine ("EvServer INIT\n");
    _system = ActorSystem.Create("local-sys");
    Core = _system.ActorOf("core");
    } public void Stop()
    {
    //Console.WriteLine("EvServer is stoped\n");
    //_system.Stop(Core);
    //Akka.Actor.
    }
    }
    }
    Server.cs using Akka.Actor; using System.Net; using System.Net.Sockets; using EvServerSystem.AppSock; using System; using Akka.Monitoring; using System.Net; using System.Net.Sockets; using System.IO; namespace EvServerSystem
    {
    ///
    /// Main server actor. Akka address: /user/local-sys
    ///
    public class Server : TypedActor,IHandle
    {
    ///
    /// Initializes a new instance of the class.
    /// Reads settings, initiates DB connection and new .
    ///


    71 public Server ()
    {
    //Получение конфигурации соединений из XML-файла var settings = SettingsStorage.Instance; if (settings == null) {
    Console.WriteLine ("E: Please fill in settings and restart the app!");
    SettingsStorage.Save ();
    Console.ReadLine ();
    Environment.Exit (1);
    }
    //Соединение с БД if (settings.ConnectionString != "") { try {
    //DB.DBConnection.CreateConnection
    (settings.ConnectionString);
    } catch (Exception e) {
    Console.WriteLine (String.Format ("E: Can't connect to
    Cache DB: {0}", e.Message));
    Console.WriteLine ("Bailing out");
    Console.ReadLine ();
    Environment.Exit (2);
    }
    }
    //Ожидание соединения с клиентскими приложениями (pull-метод) var ep = new IPEndPoint (IPAddress.Any, (int)settings.AppSocketPort); var ssock = Context.ActorOf (Props.Create (() => new
    SSock.ServerSocketActor (ep)), "server-socket"); ssock.Tell (true);
    Console.WriteLine ("I: Ready to accept connections");
    //Подключение к компанентам, работующим в режиме сервера (push-метод) try
    { string component = settings.PassiveComponents.Split(';')[0]; string id_component = component.Split(',')[0]; string address_component = component.Split(',')[1]; string ip_component = address_component.Split(':')[0]; string port_component = address_component.Split(':')[1]; var ep_component = new IPEndPoint(IPAddress.Parse(ip_component),
    Convert.ToInt16(port_component));
    TcpClient _client = new TcpClient();
    _client.Connect(ep_component); var _stream = _client.GetStream();
    StreamReader _sr = new StreamReader(_stream);
    } catch (Exception ex)
    {
    Console.WriteLine("E: Could not connect to component!");
    Console.WriteLine(ex.Message);
    Console.ReadLine();
    Environment.Exit(1);
    }
    }
    #region IHandle implementation

    72
    ///
    /// Handles the message from .
    ///

    ///
    Application's TCP socket.
    public void Handle (TcpClient message)
    {
    //got client from ServerSocket
    //AppSocketActor address: /user/core/IP var appsock = Context.ActorOf (String.Format ("app-
    {0}", message.Client.RemoteEndPoint.ToString ()));
    Console.Write(message.Client.RemoteEndPoint.ToString());
    //kick start
    //appsock.Tell(message);
    //Context.IncrementMessagesReceived ();
    }
    #endregion
    }
    }
    SettingsStorage.cs using System.IO; using System.Xml.Serialization; using System.Security.Cryptography.X509Certificates; using System.Xml; using InterSystems.Data.CacheTypes; using System.Data; namespace EvServerSystem
    {
    ///
    /// Server settings storage.
    ///

    [XmlRoot ("EvServerStartupData")] public class SettingsStorage
    {
    ///
    /// Gets or sets the connection string.
    ///

    /// The connection string. public string ConnectionString { get; set; }
    ///
    /// Gets a value indicating whether this instance is ssl enabled.
    ///

    /// true if this instance is ssl enabled; otherwise,
    false.
    public bool IsSslEnabled { get; set; }
    //var serverCertificate = new X509Certificate2(ServerCertificateFile);
    [XmlIgnore] public X509Certificate2 ServerCert{ get; private set; } public bool AcceptEncryptedOnly { get; set; }
    ///
    /// Port to listen for applications on.
    ///
    public uint AppSocketPort;

    73 public string PassiveComponents;
    [XmlIgnore] static SettingsStorage _instance;
    ///
    /// Gets the loaded instance of .
    ///

    /// Ready and loaded settings instance or null if XML file couldn't be loaded.
    [XmlIgnore] public static SettingsStorage Instance { get { if (_instance == null) {
    Load (_filePath);
    } return _instance;
    }
    } public static void InitMock ()
    {
    _instance = new SettingsStorage ();
    }
    ///
    /// Initializes a new instance of the class.
    ///
    public SettingsStorage ()
    {
    ConnectionString = "your connection string here;";//"Server=localhost;
    Port=1972; Namespace=EVENTCLASS; Password=SYS; User ID=_SYSTEM;";
    }
    [XmlIgnore] const string _filePath = "./settings.xml";
    ///
    /// Load application settings.
    ///
    public static void Load (string path)
    { var fi = new FileInfo (path); if (fi.Exists) { var myXmlReader = new StreamReader (_filePath); try {
    Deserialize (myXmlReader); myXmlReader.Close ();
    } catch {
    _instance = null;
    } finally { myXmlReader.Dispose ();
    }
    }
    } public static void LoadFromString (string data)
    {

    74
    Deserialize (data);
    } static void Deserialize (string data)
    { using (MemoryStream stream = new MemoryStream ()) {
    StreamWriter writer = new StreamWriter (stream); writer.Write (data); writer.Flush (); stream.Position = 0; using (var sr = new StreamReader (stream))
    Deserialize (sr);
    }
    } static void Deserialize (StreamReader stream)
    { var mySerializer = new XmlSerializer (typeof(SettingsStorage));
    _instance = (SettingsStorage)mySerializer.Deserialize (stream);
    }
    ///
    /// Save application settings.
    ///
    public static void Save ()
    { if (_instance == null) {
    _instance = new SettingsStorage ();
    } var mySerializer = new XmlSerializer (typeof(SettingsStorage)); var myXmlWriter = new StreamWriter (_filePath); try { mySerializer.Serialize (myXmlWriter, _instance); myXmlWriter.Dispose ();
    } finally { myXmlWriter.Dispose ();
    }
    }
    }
    }
    AppSocket.cs using System; using Akka.Actor; using System.Net.Sockets; using System.IO; using System.Text; using System.Net.Security; using System.Security.Authentication; namespace EvServerSystem.AppSock
    {
    ///
    /// Actual application socket, handles communication with applications.
    /// Sends everything to parent .
    ///
    public class AppSocket
    {

    75
    IActorRef _parent;
    TcpClient _client; string _sysKeyCode;
    Stream _stream;
    StreamReader _sr;
    StreamWriter _sw;
    TimeSpan _actorTimeout = TimeSpan.FromSeconds (1); public string SysKeyCode { get { return _sysKeyCode; } } public bool IsEncrypted { get; private set; }
    ///
    /// Initializes a new instance of the class.
    ///

    ///
    Parent actor.
    ///
    Application's TCP socket.
    public AppSocket (IActorRef sender, TcpClient client)
    {
    _parent = sender;
    _client = client;
    }
    ///
    /// Starts the connection, reading first token and entering read loop.
    ///
    public async void StartConnection ()
    {
    _stream = _client.GetStream ();
    _sr = new StreamReader (_stream, Encoding.UTF8);
    _sw = new StreamWriter (_stream, Encoding.UTF8);
    _sw.AutoFlush = true; if (_sr.ReadLineAsync ().Result != "EVAPP1") { await _sw.WriteLineAsync ("Unknown version");
    StopSocket (); return;
    } await _sw.WriteLineAsync ("301 Waiting for system ID");
    _sysKeyCode = _sr.ReadLineAsync ().Result; await _sw.WriteLineAsync ("200 Ready to accept commands");
    #if DEBUG
    Console.WriteLine ("D: Client accepted");
    #endif while (true) { string line = ""; try { line = await _sr.ReadLineAsync (); //TODO: make buffered read
    } catch {
    #if DEBUG
    Console.WriteLine ("D: Client dropped");
    #endif
    StopSocket (); return;
    }
    #if DEBUG
    Console.WriteLine (String.Format ("D: got {0}", line));
    #endif

    76 switch (line) { case "bye": await _sw.WriteLineAsync ("250 Goodbye");
    StopSocket ();
    #if DEBUG
    Console.WriteLine ("D: Client discon");
    #endif return; case "":
    _client.Close (); return; case "secure": if (SettingsStorage.Instance.IsSslEnabled) { await _sw.WriteLineAsync ("201 Upgrading"); var ssl = new SslStream (_stream); ssl.AuthenticateAsServer (SettingsStorage.Instance.ServerCert, true,
    SslProtocols.Tls12, false);
    _sr = new StreamReader (ssl, Encoding.UTF8);
    _sw = new StreamWriter (ssl, Encoding.UTF8);
    _stream = ssl;
    _sw.WriteLine ("200 Encrypted connection has been established.");
    IsEncrypted = true;
    } else await _sw.WriteLineAsync ("401 SSL is not supported."); break; default: if (SettingsStorage.Instance.AcceptEncryptedOnly && !IsEncrypted) { await _sw.WriteLineAsync ("501 Server accepts encrypted connections only.");
    StopSocket (); return;
    }
    _parent.Tell (line); var task = _parent.Ask (line, _actorTimeout); var resp = (string)task.Result; await _sw.WriteLineAsync (resp); break;
    }
    }
    } void StopSocket ()
    {
    _client.Close ();
    _parent.GracefulStop (TimeSpan.FromSeconds (30));
    }
    }
    }
    AppSocketActor.cs using System; using Akka.Actor; using System.Net.Sockets; using Newtonsoft.Json; using System.IO;

    77 using EvServerSystem.DB; using Akka.Monitoring; namespace EvServerSystem.AppSock
    {
    ///
    /// Actor wrapper for .
    /// Akka address: /user/local-sys/app/IP:PORT
    ///
    public class AppSocketActor : TypedActor,IHandle,IHandle
    {
    AppSocket _asock;
    #region IHandle implementation
    ///
    /// Handles the application's TcpClient socket.
    ///

    ///
    App socket.
    public void Handle (TcpClient message)
    {
    #if DEBUG
    Console.WriteLine ("D: new appsock");
    #endif
    _asock = new AppSocket (Context.Self, message);
    Context.IncrementCounter ("init_success"); try {
    _asock.StartConnection ();
    } catch {
    Context.Stop (Context.Self);
    }
    }
    ///
    /// Handles the string message from underlying .
    ///

    ///
    Message from application.
    public void Handle (string message)
    {
    //TODO: check if connected
    #if NOBASE
    Context.Sender.Tell ("402 Database is not available."); return;
    #else if (DB.DBConnection.Connection == null) {
    Context.Sender.Tell ("402 Database is not available."); return;
    } var reader = new JsonTextReader (new StringReader (message)); var msg = new DB.Messages.Event.ActualEvent (DBConnection.Connection); msg.KeyValue = new DB.Messages.Event.KeyValueEvent
    (DBConnection.Connection);
    //msg.ActualEventKeyValue.Message.Add(new ) var currPair = new DB.Messages.Event.KeyValuePair
    (DBConnection.Connection); while (reader.Read ()) { switch (reader.TokenType) {

    78 case JsonToken.PropertyName: currPair = new DB.Messages.Event.KeyValuePair
    (DBConnection.Connection); currPair.Key = reader.Value.ToString (); break; case JsonToken.String: if (currPair != null) { currPair.Value = reader.Value.ToString (); msg.KeyValue.Message.Add (currPair.Key, currPair); if (currPair.Key == "eventCode") { msg.EventCode = long.Parse
    (currPair.Value); long res = 0; if (!long.TryParse (currPair.Value, out res)) {
    Context.Sender.Tell (2); return;
    }
    }
    } break;
    }
    } msg.EventNameComponent = _asock.SysKeyCode.ToString ();
    //msg.EventCode =
    //Context.ActorSelection ("/user/local-sys/db").Tell (msg); var rval = msg.Commit ().Value;
    Context.Sender.Tell (rval);
    #if DEBUG
    Console.WriteLine (String.Format ("D: return value {0}", rval));
    #endif
    #endif
    }
    #endregion
    ///
    /// Monitor unhandled messages. Normally should never happen
    ///

    /// protected override void Unhandled (object message)
    {
    Context.IncrementUnhandledMessage (); base.Unhandled (message);
    }
    ///
    /// Monitor actor startup.
    ///
    protected override void PreStart ()
    {
    Context.IncrementActorCreated ();
    }
    ///
    /// Monitor actor kills.
    ///
    protected override void PostStop ()

    79
    {
    Context.IncrementActorStopped ();
    }
    }
    }
    DBConnection.cs using InterSystems.Data.CacheClient; namespace EvServerSystem.DB
    {
    ///
    /// Connection to Cache database.
    ///
    public static class DBConnection
    { static CacheConnection _cacheConnect;
    ///
    /// Read-only connection.
    ///

    /// The connection. public static CacheConnection Connection { get { return _cacheConnect; }
    }
    ///
    /// Creates the connection from specified Cache ConnectionString.
    ///

    ///
    Connection string.
    public static void CreateConnection (string connString)
    {
    _cacheConnect = new CacheConnection ();
    _cacheConnect.ConnectionString = connString;
    _cacheConnect.Open ();
    System.Console.WriteLine("I: DB connection created.");
    }
    }
    }
    ServerSocket.cs using System.Net.Sockets; using Akka.Actor; using System.Net; using System.Threading; using System.Threading.Tasks; namespace EvServerSystem.SSock
    {
    ///
    /// Server socket itself, listens for applications to connect and throws each one at parent actor.
    ///
    class ServerSocket
    { readonly IActorRef _actor;

    80 readonly TcpListener _listener; readonly CancellationTokenSource _cancelsrc = new CancellationTokenSource (); public ServerSocket (IActorRef actor, IPEndPoint ep)
    {
    _actor = actor;
    _listener = new TcpListener (ep);
    }
    ///
    /// Start listening.
    ///
    public async void Start ()
    {
    _listener.Start (); await AcceptConnections ();
    }
    ///
    /// Stop listening.
    ///
    public void Stop ()
    {
    _cancelsrc.Cancel ();
    }
    ///
    /// Main listening loop.
    ///

    /// Applications' sockets. async Task AcceptConnections ()
    { while (!_cancelsrc.IsCancellationRequested) { var cli = await _listener.AcceptTcpClientAsync ();
    _actor.Tell (cli);
    } return;
    }
    }
    }
    ServerSocketActor.cs using System; using Akka.Actor; using EvServerSystem.SSock; using System.Net; using System.Net.Sockets; using Akka.Monitoring; namespace EvServerSystem.SSock
    {
    ///
    /// Actor of main server socket actor, manages underlying ServerSocket.
    ///
    public class ServerSocketActor : TypedActor,IHandle,IHandle
    {

    81
    ServerSocket _ssock; readonly IPEndPoint _ep;
    ///
    /// Initializes a new instance of the class.
    ///

    ///
    IPEndPoint of address to bind to.
    public ServerSocketActor (IPEndPoint ep)
    {
    Console.WriteLine ("I: Application socket created.");
    _ep = ep;
    }
    #region IHandle implementation
    ///
    /// New client connected and ServerSocketActor receives its socket.
    ///

    ///
    Client socket public void Handle (TcpClient message)
    {
    //redirect incoming client to main server
    Context.Parent.Tell (message);
    Context.IncrementMessagesReceived();
    #if DEBUG
    Console.WriteLine ("D: Client socket received");
    #endif
    }
    ///
    /// Handles bool message; post-start hook to create instance.
    ///

    ///
    Anything.
    public void Handle (bool message)
    {
    _ssock = new ServerSocket (Context.Self, _ep);
    _ssock.Start ();
    }
    #endregion
    ///
    /// Monitor unhandled messages. Normally should never happen
    ///

    /// protected override void Unhandled(object message)
    {
    Context.IncrementUnhandledMessage(); base.Unhandled(message);
    }
    ///
    /// Monitor actor startup.
    ///
    protected override void PreStart()
    {
    Context.IncrementActorCreated();
    }

    82
    ///
    /// Monitor actor kills.
    ///
    protected override void PostStop()
    {
    Context.IncrementActorStopped();
    }
    }
    }

    83
    КОДЫ МОДУЛЬНЫХ ТЕСТОВ
    Приложение В

    84 using NUnit.Framework; using System; using System.Net; using System.Net.Sockets; using System.IO; using EvServerSystem.AppSock; using System.Threading.Tasks; using Akka.Actor; using EvServerSystem; using EvServerSystem.SSock; using System.Runtime.InteropServices; namespace VS_test
    {
    [TestFixture] public class Test
    {
    [Test] public void AppSocketTest()
    {
    { var ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 32767); var _listener = new TcpListener(ep);
    _listener.Start() ;
    // Server ’ s client ep var sept = _listener.AcceptTcpClientAsync(); var client = new TcpClient(); client.Connect(ep); var writer = new StreamWriter(client.GetStream()); var reader = new StreamReader(client.GetStream());
    SettingsStorage.InitMock(); writer.AutoFlush = true;
    { var system = ActorSystem.Create("local−sys"); var asa = system.ActorOf("mock"); asa.Tell(sept.Result) ;
    } writer.WriteLine("EVAPP1");
    Assert.AreEqual(reader.ReadLine(), "301 Waiting for system ID" , "Connection proto check "); writer.WriteLine("test−app");
    Assert.AreEqual(reader.ReadLine(), "200 Ready to accept commands" ,
    "Connection proto check "); writer.WriteLine("gibberish foo"); var resp = reader.ReadLine();
    Assert.AreEqual(resp ,"402 Database is not available." ,"Connection proto check");
    _listener.Stop();
    }
    { var ep = new IPEndPoint ( IPAddress.Parse("127.0.0.1"), 32767); var _listener = new TcpListener(ep);
    _listener.Start(); // Server ’ s client ep var sept = _listener.AcceptTcpClientAsync(); var client = new TcpClient(); client.Connect(ep); var writer = new StreamWriter(client.GetStream()); var reader = new StreamReader(client.GetStream());
    SettingsStorage.InitMock();

    85 writer.AutoFlush = true;
    { var system = ActorSystem.Create(" local−sys "); var asa = system.ActorOf("mock"); asa.Tell(sept.Result);
    } writer.WriteLine("something something");
    Assert.Throws(() => reader.ReadLine () ,"should close connection on wrong entry str");
    _listener.Stop();
    }
    } class MockServerSocketParent:UntypedActor
    { public MockServerSocketParent(IPEndPoint obj)
    { var ssock = Context.ActorOf(Props.Create(() => new ServerSocketActor(obj)),
    "server−socket"); ssock.Tell(true);
    } object _lastObj; protected override void OnReceive(object obj)
    {
    Console.WriteLine(String.Format("Mock ServerSockParent got {0}", obj.GetType().ToString())); if (obj is bool)
    {
    Context.Sender.Tell(_lastObj);
    }
    _lastObj = obj;
    }
    }
    [Test] public void ServerSocketTest()
    { var sys = ActorSystem.Create("mock−sys"); var ep = new
    IPEndPoint(IPAddress.Any, (int)32768); var mockp = sys.ActorOf(Props.Create(() => new MockServerSocketParent(ep)),
    "mock−parent");
    System.Threading.Thread.Sleep(500); var client = new TcpClient(); client.Connect(IPAddress.Parse("127.0.0.1"), (int)32768);
    System.Threading.Thread.Sleep(1000); var connt = mockp.Ask(true, TimeSpan.FromSeconds(5)); var conn = connt.Result;
    Assert.IsTrue(conn is TcpClient, String.Format("Got {0}",conn.GetType()));
    }
    [Test] public void SettingsStorageTest()
    {
    Assert.Throws(() =>
    SettingsStorage.LoadFromString("erroneous xml"));
    }
    }
    }
    1   2   3   4


    написать администратору сайта