Система проведения аудиоконференций

Принципы и основные этапы создания сетевого приложения, обеспечивающего возможность проведения аудиоконференций, требования к нему, внутренняя структура. Команды серверной части и их назначение. Составление алгоритмов, выбор программного обеспечения.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык русский
Дата добавления 28.04.2014
Размер файла 1,3 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Введение

Компьютерные сети в настоящий момент представляют собой сложные комплексы с множеством поддерживаемых протоколов передачи данных и управелния, которые интенсивно совершенствуются. Компьютерные сети предоставляют пользователям сервисы, реализуемые в виде сетевых приложений. Одним из наиболее распространенных классов сетевых приложений являются клиент-серверные приложения [1], которые реализуются в виде клиентской части, реализующей запросы к серверам, и серверной части, реагирующей на эти запросы (рис. 1.).

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 1. Клиент-серверное приложение

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

· Команда о передаче данных авторизации на сервер.

· Команда об отключении клиента.

· Команда о подключении исходящего аудиопотока.

· Команда о подключении входящего аудиопотока.

· Команда о передаче аудиоданных.

· Команда обновления данных о конференции.

1. Исследовательская часть

1.1 Цель разработки и основные решаемые задачи

программный алгоритм приложение серверный

Главной целью данной работы является создание серверной и клиентской частей системы проведения аудиоконференций с возможностью работы в локальной или глобальной сети.

Серверная часть должна обеспечивать эффективную работу с клиентами в условиях одновременного подключения нескольких пользователей, а так же обладать дружественным интерфейсом и отвечать следующим основным требованиям:

· Авторизация пользователей в системе.

· Отправка корректных команд клиенту.

· Получение аудиоданных от клиентов.

· Микширование аудиоданных.

· Передача микшированных аудиоданных клиентам.

· Передача клиентам информации об участниках конференции.

· Сохранение в лог файл истории событий.

· Отслеживание ошибок соединения и передачи (обработка исключительных ситуаций).

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

· Корректное реагирование на команды сервера.

· Авторизация на сервере.

· Запись аудиоданных с микрофона.

· Передача аудиоданных на сервер.

· Воспроизведение полученных с сервера аудиоданных.

· Отображение информации об участниках конференции.

· Сохранение в лог файл истории событий.

· Отслеживание ошибок соединения и передачи (обработка исключительных ситуаций).

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

1.2 Современные средства решения поставленной задачи

В настоящее время существует некоторое количество приложений, предоставляющих возможность проведения аудиоконференций. Рассмотрим некоторые из них.

Asterisk

Asterisk - это проект с открытым исходным кодом компании Digium.

Данный проект:

· обладает всеми возможностями классической АТС

· поддерживает множество VoIP протоколов

· предоставляет функции

§ голосовой почты

§ конференций

§ интерактивного голосового меню (IVR)

§ центра обработки вызовов (постановка звонков в очередь и распределение их по агентам используя различные алгоритмы)

· не требует никакого специального оборудования практически для всех устройств Voice over IP

Недостатки:

· Приложение работает на операционных системах GNU/Linux, FreeBSD (Рис. 1.2.1.1) и Solaris

· Сложность в администрировании. Администратору системы требуется обладать знаниями в Asterisk и хорошими навыками администрирования Linux, знать технологии и продукты IP телефонии.

Рис. 1.2.1.1 инструмент управления Астериск FreePBX.

XMPP

XMPP (Extensible Messaging and Presence Protocol - расширяемый протокол обмена сообщениями и информацией о присутствии), ранее известный как Jabber (Рис. 1.2.2.1) - основанный на XML.

· Открытый, свободный для использования протокол для мгновенного обмена сообщениями и информацией о присутствии в режиме, близком к режиму реального времени;

· Помимо передачи текстовых сообщений, поддерживает передачу голоса, видео и файлов по сети;

· Не требует зависимости от центрального сервера. Каждый собственный сервер может функционировать обособленно;

· Стыкуется с разнообразными оперативными системами, что делает его доступным для любых компьютеров и пользователей;

Рис. 1.2.2.1 Окно обмена сообщениями в Jabber

Skype

Skype - бесплатное проприетарное программное обеспечение с закрытым кодом, обеспечивающее шифрованную голосовую связь через Интернет между компьютерами (VoIP), а также платные услуги для звонков на мобильные истационарные телефоны (Рис. 1.2.3.1).

· Программа также позволяет совершать конференц-звонки (до 25 голосовых абонентов, включая инициатора)

· Программные клиенты Skype выпущены для операционных систем[1]: Windows, Mac OS X, Linux, iOS, Windows Mobile, Google Android, PSP, Symbian.

Недостатки:

· Использование проприетарного протокола, несовместимого с открытыми стандартами (такими, как SIP илиH.323).

· постоянная передача данных (даже в ситуациях, когда сама программа находится в режиме ожидания);

· Как и любая сеть, работающая по принципу P2P, Skype подвержен вирусным эпидемиям.

Рис. 1.2.3.1 инструмент управления Skype

программный алгоритм приложение серверный

Для создания полноценной системы проведения аудиоконференций, нужно изучить несколько различных приложений, ориентированных на эту цель, выделить из их функционала самые необходимые возможности и включить их в свой проект. Кроме того следует позаботиться об удобстве реализации этих возможностей и о простоте интерфейса. Это займет довольно много времени, поэтому был выбран другой путь - создать приложение, включающее только самые необходимые возможности для проведения аудиоконференций.

В рамках данной работы будет реализовано простейшее приложение для проведения аудиоконференций. Оно обладает простым интерфейсом и ограниченными возможностями:

· Авторизация.

· Запись аудиоданных.

· Отправка аудиоданных на сервер.

· Микширование аудиоданных на сервере.

· Отправка микшированных аудиоданных клиентам.

· Воспроизведение аудиоданных клиентом.

· Отображение состава участников аудиоконференции.

Прием и отправка команд в созданном приложении происходит через специально разработанный протокол.

Представленное в данной работе приложение может быть использовано в небольшой локальной сети для общения или обсуждения проблем людьми, находящимися в различных помещениях.

2. Конструкторская часть

2.1 Блок-схемы алгоритмов

Блок-схема алгоритма подключения клиента к серверу

Блок-схема алгоритма для клиентской части. При подключении клиента к серверу создается три соединения: для передачи аудиоданных на сервер, для приема аудиоданных с сервера и управляющее соединение. Данные, относящиеся к каждому соединению, обрабатываются в отдельных потоках (Рис. 2.1.1.1).

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 2.1.1.1. Блок-схема алгоритма подключения клиента к серверу

Блок-схема алгоритма работы управляющего потока клиента

Блок-схема алгоритма для клиентской части. Управляющий поток посылает запрос о следующей команде на сервер, получает эту команду и выполняет ее (Рис. 2.1.2.1.).

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 2.1.2.1. Блок-схема алгоритма работы управляющего потока клиента

Блок-схема алгоритма работы потока отправки аудиоданных клиента

Блок-схема алгоритма для клиентской части. Передача аудиоданных между клиентом и сервером осуществляется блоками фиксированной длины, которые соответствует фиксированному времени воспроизведения. Аудиоданные накапливаются в буфере. Когда буфер достигает размера равного или большего чем блок передачи данных, то блок извлекается из буфера и отправляется на сервер. В рамках данного алгоритма реализуется простейший механизм синхронизации записи и отправки данных, основанный на понимании того, что для непрерывного воспроизведения аудиоданных, необходимо, чтобы отправка этих данных происходила через фиксированные промежутки времени. Аудиоданные поступают в буфер отправки и если он постепенно увеличивается в размере, это означает, что отправка данных происходит медленнее чем запись. В таком случае буфер очищается, что приводит к потере части данных, но позволяет избежать рассинхронизации записи и отправки данных, то есть отправки на сервер неактуальных данных (Рис. 2.1.3.1.).

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 2.1.3.1. Блок-схема алгоритма работы потока отправки аудиоданных

Блок-схема алгоритма работы потока получения аудиоданных клиента

Блок-схема алгоритма для клиентской части. Клиент получает аудиоданные в виде блоков фиксированной длины, которые соответствуют фиксированному времени воспроизведения. Клиент получает блок данных и помещает его в буфер воспроизведения. В данном алгоритме реализован простейший механизм синхронизации получения данных и воспроизведения, основанный на понимании того, что запись и воспроизведение данных должны занимать одинаковое время. Если буфер воспроизведения постепенно увеличивается, это означает, что аудиоданные поступают с сервера быстрее, чем они воспроизводятся на клиенте. Так как запись не может производиться быстрее, чем воспроизведение, то это говорит о том, что поступающие данные образовались в результате задержек при передаче и являются неактуальными. В таком случае буфер очищается (Рис. 2.1.4.1.).

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 2.1.4.1. Блок-схема алгоритма работы потока получения аудиоданных

Блок-схема алгоритма подключения клиентов сервером

Блок-схема алгоритма для серверной части. Сервер ожидает подключения клиентов. Как только клиент устанавливает первое соединение, это соединение считается управляющим, клиент получает команду на авторизацию и, если она происходит успешно, получает команду на установку соединений входящих и исходящих аудиоданных и помещается в список клиентов, ожидающих установки этих соединений. Затем клиент устанавливает соединения для входящих и исходящих аудиоданных. После установки всех трех соединений, клиент помещается в список подключенных клиентов и начинает передавать и получать аудиоданные, а также команды через командное соединение (Рис. 2.1.5.1.).

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 2.1.5.1. Блок-схема алгоритма подключения клиентов сервером.

Блок-схема алгоритма передачи аудиоданных клиентам

Блок-схема алгоритма для серверной части. Сервер передает данные клиентам блоками фиксированной длины, которые соответствуют фиксированному времени воспроизведения. Передача происходит через фиксированные промежутки времени. В момент передачи сервер получает аудиоданные из персональных буферов клиентов, совмещает эти данные и передает клиентам (Рис. 2.1.6.1.).

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 2.1.6.1. Блок-схема алгоритма передачи аудиоданных клиентам

Диаграмма протокола

Для приема и отправки команд серверной и клиентской части в создаваемом приложении был разработан специальный протокол вида (Рис. 2.2.1.):

<команда> <данные>

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

Рис. 2.2.1. Диаграмма протокола

2.2 Функциональная структура

Для корректной работы проекта, скорости выполнения команд, а также для упрощения механизма отладки, программный код разбит на небольшие функции. Самыми важными из них, для обеспечения работоспособности всего запланированного функционала, являются:

1. Исключительно для сервера:

· IServer. Start - создание TCP сервера и запуск прослушивания подключений.

· IServer. Stop - остановка сервера и отключение всех клиентов.

· Server. ListenForClients - прослушивание подключений клиентов

· Server. HandleClient - обработка подключения клиента

· Server. Playback - отправка данных клиентам

· IClientHandler. Start - запуск работы обработчика клиентских данных

· IClientHandler. Stop - остановка обработчика клиентских данных

· ClientHandler. ControlThread - поток управления

· ClientHandler. InputThread - поток входящих данных

· ClientHandler. OutputThread - поток исходящих данных

2. Исключительно для клиента:

· IClient. Start - подключение к серверу

· IClient. Stop - отключение от сервера

· IClient. AddAudioData - получение данных с аудиоустройства

· Client. PlaybackThread - поток воспроизведения

· Client. InputThread - поток входящих данных

· Client. OutputThread - поток исходящих данных

· Client. ClientThread - управляющий поток

3. Классы важные для обеспечения корректной работоспособности обоих приложений:

· Command - команда в сообщении протокола.

· Data - данные в сообщении протокола

· AudioStream - поток аудиоданных

· AudioData - аудиоданные

После рассмотрения различных вариантов реализации проекта, нами были выбраны рассмотренные в конструкторской части алгоритмы работы приложения.

Кроме того был разработан специальный протокол для решения поставленных задач, позволяющий клиенту и серверу взаимодействовать друг с другом.

Также было рассмотрено, какие функции необходимо реализовать в первую очередь для решения базовых задач, входящих в функционал проекта. Они были выделены в отдельные модули и далее реализованы в программном коде.

3. Технологическая часть

3.1 Средства программирования и отладки

Для разработки клиент-серверного приложения был выбрана платформа.net и язык C#. Данный выбор обусловлен наличием библиотек, предоставляющих необходимый для нашего проекта функционал, реализованный в достаточном объеме, а также удобством языка.

Средой разработки был выбран Microsoft Visual Studio 2008 из-за обширности и разнообразия компонентных моделей, проработанных за долгие годы существования среды.

Для записи и воспроизведения аудиоданных выбрана библиотека NAudio из-за простоторы использования и наличия открытого доступа к ней.

3.2 Основные структуры данных программного комплекса

Протокол

Создаваемое приложение имеет ряд запросов, которые сервер и клиент посылают друг другу. Структура этих запросов:

Command [Data]

Где Command содержит в себе тип, которые определеят содержание данных в Data. Соответствия Command и Data приведены ниже (запросы, начинающиеся с Client посылает клиент, а начинающиеся с Server посылает сервер):

· «ClientLogin DataClientLogin» - передача данных авторизации.

· «ClientLogout» - отключение клиента.

· «ClientConnectInput DataInteger» - подключение соединения входящих аудиоданных (в поле данных идентификатор соединения).

· «ClientConnectOutput DataInteger» - подключение соединения исходящих аудиоданных (в поле данных идентификатор соединения).

· «ClientSendAudioData DataAudio» - передача аудиоданных.

· «ClientAction» - запрос следующего действия у сервера.

· «ServerLoginAccepted DataInteger» - сообщение об успешной авторизации и передача идентификатора соединения.

· «ServerAccepted» - сообщение об успешной операции.

· «ServerDenied DataString» - сообщение о неудачной операции и причина.

· «ServerClearUserList» - указание очистить список пользователей.

· «ServerUserOnline DataUser» - сообщение о том, что ползователь подключился к конференции.

· «ServerSendAudioData AudioData» - отправление аудио данных.

· «ServerLogout» - указание отлкючиться от сервера.

· «ServerNone» - указание ничего не делать.

3.3 Описание работы приложения

Начало работы

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

Для того чтобы запустить приложение-сервер, необходимо запустить по ACServer.exe файл, после чего появится окно программы. Чтобы начать прослушивание необходимо ввести порт, через который будет работать приложение. После ввода нужной информации необходимо нажать на кнопку «Пуск» (Рис. 3.3.1.).

Рис. 3.3.1. Окно приложения-сервера

Чтобы запустить, приложение-клиент, необходимо запустить файл по ACClient.exe файлу, после чего откроется окно программы. В открывшемся окне нужно выбрать вкладку «Настройки». На данной вкладке необходимо установить IP-адрес сервера, порт, имя пользователя и пароль. Также необходимо выбрать устройство записи аудио с помощью кнопки «Выбрать».

Для того, чтобы установить соединение с компьютером-сервером необходимо нажать кнопку «Подключиться» (Рис. 3.3.2.).

Рис. 3.3.2. Окно приложения-клиента

Информация о пользователях и просмотр событий на сервере

Информацию о зарегестрированных на сервере пользователях можно увидеть в окне приложения сервера. Добавить новых пользователей можно, занеся необходимую информацию в файл app.config, который находится в одной дирректории с файлом приложения сервера. Информацию о событиях сервера можно увидеть в окне «Журнал» приложения сервера (Рис. 3.3.3.).

Рис. 3.3.3. Информация о пользователях и событиях на сервере

Информация о пользователях и просмотр событий на клиенте

После того, как пользователь установит связь с сервером, в окне начнется передача аудиоданных, а в окне приложения-клиента появится список пользователей, подключенных к серверу в данный момент (Рис. 3.3.4.).

Рис. 3.3.4. Окно приложения-клиента. Список участников

Информацию о событиях клиента можно увидеть на вкладке «Журнал» (Рис. 3.3.5.).

Рис. 3.3.5. Окно приложения-клиента. Журнал

Тестирование проекта

Проект был тщательно протестирован. Были выявлены следующие недостатки:

· Воспроизведение аудиоданных библиотекой NAudio происходит медленнее, чем запись, поэтому даже при очень быстром соединении появляется задержка. Она ликвидируется с помощью механизма синхронизации получения данных и воспроизведения, что приводит к частичной потере данных и ухудшению качества воспроизведения.

· Простота мехнизма формирования исходящих аудиоданных сервером приводит к возникновению задержек воспроизведения, которые ликвидируются механизмами синхронизации, что приводит к ухудшению качества воспроизведения.

В целом, приложения работают стабильно, скорость посылки и выполнения команд не вызывает нареканий.

По завершении работы над проектом, была разработана демо-версия приложения, обладающая простым интерфейсом и включающая в себя все запланированные в начале функции, из которых полностью реализованы:

· Передача команд сервера на клиент.

· Выполнения команд сервера клиентом, а именно:

· Авторизация.

· Отправка аудиоданных.

· Получение аудиоданных.

· Воспроизведение аудиоданных.

· Сохранение всей истории работы в лог-файл.

Основными направлениями совершенствования проекта в дальнейшем будут:

· Введение более сложного механизма синхронизации, который позволит улучшить качество получаемых клиентом аудиоданных.

· Увеличение частоты дискретизации записываемых аудиоданных.

· Оптимизация существующих алгоритмов и введение новых (например - сжатия аудиоданных) для увеличения скорости передачи данных.

Заключение

В рамках данного проекта было разработано клиент-серверное приложение, предназначенное для проведения аудиоконференций.

Функциональность приложения обусловлена базовыми требованиями к программам такого рода и включает в себя:

1. Возможность передачи сообщений с сервера на клиентское приложение.

2. Возможность записи аудиоданных на клиентском приложении.

3. Возможность передачи аудиоданных серверному приложению.

4. Возможность работы с несколькими клиентами серверным приложением.

5. Возможность объединения аудиоданных клиентов и отправка этих данных клиентам.

6. Возможность воспроизведения аудиоданных клиентским приложением.

Перед началом работы были рассмотрены насколько аналогичных приложений. Их реализация сравнивалась по нескольким критериям: необходимость, удобство, скорость и др. После данного анализа были выделены функции, необходимые для реализации базового проекта.

Разработанная программа обладает простым пользовательским интерфейсом, который без дополнительных объяснений отображает всю функциональность проекта.

Выполненные задачи отражают основные принципы реализации аудиоконференции. Таким образом, были отработаны базовые механизмы проведения аудиоконференций.

Приложение 1

Исходные программные коды

Класс клиента

// Класс представляющий функциональность клиента

public class Client: IClient

{

ACNPClient m_acnpClient = null; // Управляющее соединение

ACNPClient m_acnpInput = null; // Входящий поток аудиоданных

ACNPClient m_acnpOutput = null; // Исходящий поток аудиоданных

private bool m_disconnect = true; // Соединение разорвано

private bool m_transmit = false; // Передача данных разрешена

IWavePlayer m_iwp = null; // Проигрыватель аудиоданных

ACBuffer m_sendBuffer = new ACBuffer(); // Буфер отправки аудиоданных

System. Threading. AutoResetEvent m_sendBufferEvent = new AutoResetEvent(false); // Событие отправки данных

// Событие получения данных

System. Threading. AutoResetEvent m_recieveBufferEvent = new AutoResetEvent(false);

// Событие воспроизведения аудиоданных

System. Threading. AutoResetEvent m_playbackEvent = new AutoResetEvent(false);

// Обратная связь для агрегирующего класса

IClientCallbacks m_callbacks = null;

// Потоки управления, входящих, исходящих данных и воспроизведения

Thread m_clientThread = null;

Thread m_outputThread = null;

Thread m_inputThread = null;

Thread m_plabackThread = null;

// Деинициализация: закрываются соединения, уничтожаются потоки

void CloseAllConnections()

{

m_transmit = false;

m_disconnect = true;

if (m_inputThread!= null)

{

AbortThread (m_inputThread);

m_inputThread = null;

}

if (m_outputThread!= null)

{

AbortThread (m_outputThread);

m_outputThread = null;

}

if (m_plabackThread!= null)

{

AbortThread (m_plabackThread);

m_plabackThread = null;

}

if (m_iwp!= null)

{

m_iwp. Stop();

m_iwp = null;

}

if (m_acnpClient!= null)

{

m_acnpClient. Client. Close();

m_acnpClient = null;

}

if (m_acnpInput!= null)

{

m_input. Client. Close();

m_acnpInput = null;

}

if (m_acnpOutput!= null)

{

m_acnpOutput. Client. Close();

m_acnpOutput = null;

}

if (m_callbacks!= null)

{

m_callbacks. OnDisconnect();

}

if (m_clientThread!= null)

{

AbortThread (m_clientThread);

m_clientThread = null;

}

}

void AbortThread (Thread thread)

{

try

{

thread. Abort();

}

catch (Exception ex)

{

}

}

// Начало работы. Установление соединений с сервером.

//Config: параметры соединения (адрес, порт…)

bool IClient. Start (Config config)

{

m_transmit = false;

m_disconnect = false;

m_clientThread = new Thread (new ParameterizedThreadStart(ClientThread));

m_clientThread. Start(config);

return true;

}

// Завершение работы клиента

bool IClient. Stop()

{

if (m_acnpClient!= null)

{

CloseAllConnections();

}

return true;

}

// Управляющий поток

private void ClientThread (object objConfig)

{

Config config = (Config) objConfig;

// Создание управляющего соединения

m_acnpClient = new ACNPClient();

try

{

m_acnpClient. Connect (config. Address, config. Port);

}

catch (SocketException ex)

{

CloseAllConnections();

return;

}

DataInteger loginData = m_acnpClient. Login (config. UserName, config. Password);

if (loginData == null)

{

CloseAllConnections();

return;

}

// Создание соединения входящих данных

m_acnpInput = new ACNPClient ();

try

{

m_acnpInput. Connect (config. Address, config. Port);

}

catch (SocketException ex)

{

CloseAllConnections();

return;

}

// Создание соединения исходящих данных

m_acnpOutput = new ACNPClient();

try

{

m_acnpOutput. Connect (config. Address, config. Port);

}

catch (SocketException ex)

{

CloseAllConnections();

return;

}

m_recieveBufferEvent. Reset();

m_sendBufferEvent. Reset();

// Запуск потоков соединений входящих и исходящих данных

m_outputThread = new Thread (new ThreadStart(OutputThread));

m_outputThread. Start();

m_inputThread = new Thread (new ThreadStart(InputThread));

m_inputThread. Start();

m_transmit = true;

// управляющий цикл

// управляющий клиент запрашивает действие у сервера и выполняет его

bool localDisconnect = true;

while (! m_disconnect)

{

Command request = new Command (Command. CommandType. ClientAction);

m_acnpClient. SendCommand(request);

Command response = m_acnpClient. RecieveCommand();

if (response == null)

{

m_disconnect = true;

CloseAllConnections();

return;

}

switch (response. Value)

{

case Command. CommandType. None:// Ничего не делать

Thread. Sleep(3000);

break;

case Command. CommandType. ServerLogout:// Разорвать соединение

m_transmit = false;

m_disconnect = true;

localDisconnect = false;

CloseAllConnections();

return;

break;

case Command. CommandType. ServerClearUserList:// Очистить контакт-лист

m_callbacks. OnContactListClear();

break;

case Command. CommandType. ServerUserOnline:// Новый пользователь

DataString userName = m_acnpClient. RecieveData<DataString>();

m_callbacks. OnContactListAddUser (userName. Value);

break;

}

}

// В случае, если клиент самостоятельно разорвал соединение

// необходимо послать уведомление серверу

if (localDisconnect)

{

Command request = new Command (Command. CommandType. ClientLogout);

m_acnpClient. SendCommand(request);

}

}

// Поток исходящих данных

private void OutputThread()

{

while (! m_disconnect)

{

if (m_transmit)

{

// Ждем, пока данные не поступят в буфер

m_sendBufferEvent. WaitOne();

AudioData audioData = m_sendBuffer. GetNext();

m_acnpOutput. SendAudioData(audioData);

}

}

}

// Поток входящих данных

private void InputThread()

{

AudioStream ws = new AudioStream (new WaveFormat (8000, 1));

m_plabackThread = new Thread (new ParameterizedThreadStart(PlayBackThread));

m_plabackThread. Start(ws);

while (! m_disconnect)

{

if (! m_transmit)

{

continue;

}

AudioData data = m_acnpInput. RecieveAudioData();

if (data == null)

{

continue;

}

ws. Add(data);

if (ws. IsReady())

{

PlaybackState ps = PlaybackState. Stopped;

// неизвестное поведение библиотеки NAudio

// иногда при чтении свойства возникает исключение

try

{

ps = m_iwp. PlaybackState;

}

catch (Exception ex)

{

string str = ex. Message;

}

if (ps!= PlaybackState. Playing)

{

m_recieveBufferEvent. Set();

}

}

}

}

// Поток воспроизведения

private void PlayBackThread (object wsObj)

{

AudioStream ws = (AudioStream) wsObj;

m_iwp = new DirectSoundOut();

BlockAlignReductionStream bs = new BlockAlignReductionStream(ws);

m_iwp. Init(ws);

m_iwp. PlaybackStopped += new EventHandler(OnPlaybackStopped);

while (! m_disconnect)

{

m_recieveBufferEvent. WaitOne();

if (ws. IsReady())

{

if (m_iwp. PlaybackState!= PlaybackState. Playing)

{

m_iwp. Play();

}

}

}

}

// Логирование сообщения

private void Log (string message)

{

Manager. Log(message);

}

#region IAudioReciever Members

// Счетчик поступлений аудио блоков

private int m_c = 0;

// Установка класса подписчика на события клиента

void IClient. SetCallbacks (IClientCallbacks callbacks)

{

m_callbacks = callbacks;

}

// Получение аудиоданных от библиотеки NAudio

void IClient. AddAudioData (byte[] buffer, int count)

{

if (m_disconnect ||! m_transmit)

{

return;

}

m_c++;

// Каждый 40-й блок мы отбрасываем, поскольку воспроизведение

// происходит медленнее чем запись. Число определено эксперементально.

if (m_c% 40 == 0)

{

return;

}

m_sendBuffer. Add (buffer, count);

if (m_sendBuffer. IsReady())

{

m_sendBufferEvent. Set();

}

}

#endregion

}

Класс сервера

// Класс представляющий функциональность сервера

public class Server: IServer, IClientHandlerCallbacks

{

// Объект, ожидающий подключений клинтов

private TcpListener m_listener = null;

// Поток прослушивания клинтов

private Thread m_listenThread = null;

// Список подключенных пользователей

private List<User> m_users = new List<User>();

// Генератор случайных чисел для идентификатора пользователя

private Random m_random = new Random(0);

// Список авторизованных клиентов, не установивших еще все необходимые

// соединения и ожидающих их установления.

private List<IClientHandler> m_waiting = new List<IClientHandler>();

// Список клиентов готовых к работе

private List<IClientHandler> m_ready = new List<IClientHandler>();

// Объект для синхронизации на предыдущих двух списках

private object m_handlersMutex = new object();

// Поток исходящих данных

private Thread m_playbackThread = null;

// Класс подписанных на события сервера

private IServerCallbacks m_callbacks = null;

// Состояния сервера

enum State

{

Listening,

None

}

State m_state = State. None;

// Закрываем все соединения

private void CloseAllConnections()

{

m_state = State. None;

if (m_listener!= null)

{

try

{

m_listener. Stop();

}

catch (SocketException ex)

{

Manager. Log («Сервер: Не удалось остановить прослушивание. Причина:» + ex. Message);

}

m_listener = null;

}

lock (m_ready)

{

foreach (IClientHandler handler in m_ready)

{

handler. Stop();

}

}

if (m_callbacks!= null)

{

m_callbacks. OnStop();

}

}

// Поток микширования данных

private void Playback()

{

while (m_state == State. Listening)

{

lock (m_ready)

{

int length = m_ready. Count;

int idx = m_ready. Count;

// Получаем массив, содержащий аудидоданный каждого клиента

AudioData[] data = new AudioData[idx];

AudioData merge = null;

for (int i = 0; i < idx; i++)

{

data[i] = m_ready[i].GetNext();

}

// Производим микширование аудиоданных

for (int i = idx - 1; i >= 0; i-)

{

if (data[i] == null)

{

continue;

}

if (merge == null)

{

merge = new AudioData (data[i].Count, data[i].Data);

continue;

}

merge. Merge (data[i]);

}

// Отправляем смикшированные данные обратно клиентам

for (int i = 0; i < idx; i++)

{

m_ready[i].Add(merge);

}

}

Thread. Sleep(190);

}

}

// Поток прослушивания подключений клиентов

private void ListenForClients()

{

try

{

m_listener. Start();

}

catch (SocketException ex)

{

Manager. Log («Server: Failed to start listening. Reason:» + ex. Message);

CloseAllConnections();

return;

}

Manager. Log («Сервер: Начало прослушивания» + m_listener. LocalEndpoint. ToString());

while (m_state == State. Listening)

{

TcpClient client = null;

try

{

client = m_listener. AcceptTcpClient();

}

catch (SocketException ex)

{

Manager. Log («Server: Failed to accept tcp client. Reason:» + ex. Message);

return;

}

Manager. Log («Сервер: Подключение» + client. Client. RemoteEndPoint. ToString());

Thread thread = new Thread (new ParameterizedThreadStart(HandleClient));

thread. Start(client); // Запуск установления соединения

}

}

// Поток создания соединения с клиентом

private void HandleClient (object objClient)

{

TcpClient client = (TcpClient) objClient;

ACNPServer server = new ACNPServer(client);

// Получаем запрос от клиента и в соответствии с запросом

// выполняем авторизацию или подключение входного или выходного потоков

Command command = server. RecieveCommand();

if (command == null)

{

return;

}

IClientHandler handler;

DataInteger data;

bool movedToReady = false;

switch (command. Value)

{

case Command. CommandType. ClientLogin: // Выполняем авторизацию

DataClientLogin loginData = server. GetLoginData();

if (loginData == null)

{

return;

}

// Проверям, зарегистрирован ли пользователь

User foundUser = GetUser (loginData. UserName, loginData. Password);

if (foundUser == null)

{

server. DenyLogin();

return;

}

else // Проверям есть ли в списке ожидающих клиентов

{

foreach (IClientHandler clientHandler in m_waiting)

{

if (clientHandler. GetUserName() == loginData. UserName)

{

server. DenyLogin();

return;

}

}

foreach (IClientHandler clientHandler in m_ready)

{

if (clientHandler. GetUserName() == loginData. UserName)

{

server. DenyLogin();

return;

}

}

int id = m_random. Next(1000000);

handler = new ClientHandler (server, foundUser, id);

handler. SetCallbacks(this);

m_waiting. Add(handler);

server. AcceptLogin(id);

}

break;

case Command. CommandType. ClientConnectInput:// Регистрация входящего потока

data = server. RecieveData<DataInteger>();

if (data == null)

{

return;

}

handler = GetWaitingHandler (data. Id);

if (handler == null)

{

server. SendDeny();

}

ACNPServer inputServer = new ACNPServer(client);

handler. SetOutput(inputServer);

if (handler. IsComplete())

{

MoveToReady(handler);

movedToReady = true;

}

server. SendAccept();

break;

// Регистрация исходящего потока

case Command. CommandType. ClientConnectOutput:

data = server. RecieveData<DataInteger>();

if (data == null)

{

return;

}

handler = GetWaitingHandler (data. Id);

if (handler == null)

{

server. SendDeny();

}

ACNPServer outputServer = new ACNPServer(client);

handler. SetInput(outputServer);

if (handler. IsComplete())

{

MoveToReady(handler);

movedToReady = true;

}

server. SendAccept();

break;

default:

Log («Сервер: Ошибка.»);

return;

break;

}

if (! movedToReady)

{

return;

}

RefreshContactLists();

handler. ControlThread();

}

// Обновление списка пользователей

private void RefreshContactLists()

{

lock (m_ready)

{

foreach (IClientHandler clientHandler in m_ready)

{

clientHandler. RefreshContactList();

}

}

}

// Получить список пользователей

private User GetUser (string name, string password)

{

User found = null;

lock (m_usersMutex)

{

foreach (User user in m_users)

{

if (user. IsTheSame (name, password))

{

found = user;

break;

}

}

}

return found;

}

// Получить обработчик клиента по идентификатору

private IClientHandler GetWaitingHandler (int id)

{

foreach (IClientHandler handler in m_waiting)

{

if (handler. IsYourId(id))

{

return handler;

}

}

return null;

}

// Перемещение обработчика клиента из списка ожидающих клиентов

// в списко готовых к работе клиентов

private void MoveToReady (IClientHandler handler)

{

lock (m_handlersMutex)

{

m_waiting. Remove(handler);

m_ready. Add(handler);

handler. Start();

}

}

#region IServer Members

// Подписка на события сервера

void IServer. SetCallbacks (IServerCallbacks callbacks)

{

m_callbacks = callbacks;

}

// Начало работа сервера

bool IServer. Start (Config config)

{

m_state = State. Listening;

lock (this)

{

m_users. Clear();

foreach (User user in config. Users)

{

m_users. Add(user);

}

}

m_listener = new TcpListener (IPAddress. Any, config. Port);

m_listenThread = new Thread (new ThreadStart(ListenForClients));

m_listenThread. Start();

m_playbackThread = new Thread (new ThreadStart(Playback));

m_playbackThread. Start();

return true;

}

// Окончание работы сервера

bool IServer. Stop()

{

CloseAllConnections();

Manager. Log («Server: Stopped listening.»);

return true;

}

#endregion

#region IClientHandlerCallbacks Members

// Обратный вызов от обработчика клиента, сообщающий об отключении

// клиента

void IClientHandlerCallbacks. OnDisconnect (IClientHandler handler)

{

lock (m_waiting)

{

m_waiting. Remove(handler);

}

lock (m_ready)

{

m_ready. Remove(handler);

}

RefreshContactLists();

}

// Обратный вызов от обработчика клиента, запрашивающий

// список работающих пользователей

string[] IClientHandlerCallbacks. GetUsers()

{

string[] users = null;

lock (m_ready)

{

users = new string [m_ready. Count];

for (int i = 0; i < m_ready. Count; i++)

{

users[i] = m_ready[i].GetUserName();

}

}

return users;

}

#endregion

}

Размещено на Allbest.ru


Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.