Системы мгновенного обмена сообщениями — КиберПедия 

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...

История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...

Системы мгновенного обмена сообщениями

2019-11-19 417
Системы мгновенного обмена сообщениями 0.00 из 5.00 0 оценок
Заказать работу

Постановка задачи

Используя данные методические указания, создать два сетевых приложения в среде Microsoft Visual Studio Express 2013. По результатам ЛР должно быть создано два проекта на языке C#. Клиент должен быть реализован в виде приложения Windows Forms и должен поддерживать прием и передачу текстовых сообщений по протоколу TCP. Сервер должен быть реализован в виде консольного приложения и должен обеспечивать обмен сообщениями между клиентами по протоколу TCP. Протокол TCP должен быть реализован с использованием классов TcpListener и TcpClient, а приложения должны работать в асинхронном режиме.

В отчете представляются: название работы, постановка задачи, результаты, скриншоты программы и вывод.

2. Краткая теоретическая справка

Системы мгновенного обмена сообщениями

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

Мессенджер — это клиентское приложение, используемое для обмена сообщениями.

Обычно клиенты подключаются к серверу сети обмена сообщениями. Популярностью пользуются такие мессенджеры, как IRC, AIM, ICQ, MSN, XMPP. Между различными мессенджерами обычно нет прямой связи (исключение — XMPP). В ЛР будет реализован сервер и клиент для обмена мгновенными сообщениями по протоколу TCP.

Транспортный протокол TCP

На транспортном уровне часто используется протокол TCP (Transmission Control Protocol). Этот протокол является важнейшим элементом стека протоколов TCP/IP. Существует разные реализаций данного протокола, например, TCP Tahoe, TCP Reno, TCP Vegas и проч. Алгоритмы, используемые в его работе, много раз усовершенствовались.  TCP применяется для гарантированной доставки сообщений других протоколов, например, FTP, TELNET, SMTP, DNS, HTTP.

В протоколе TCP передаваемая информация рассматривается как поток байтов. Он передается по «трубе», соединяющей два сетевых приложения. Поток разделен на сегменты.

Сегмент — это набор байтов, сформированный протоколом TCP.

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

TCP-соединение — это виртуальный канал связи между приложениями на отправителе и получателе для доставки потока байтов по протоколу TCP.

Сегменты могут быть доставлены по различным маршрутам в сети Интернет, быть повреждены и запрошены с отправителя повторно, но для пользователя TCP создаст иллюзию надежного соединения с другим узлом.

Особенности TCP

Протокол нумерует все байты, передаваемые по TCP-соединению. Для этого в заголовке сегмента (рис. 2.1) используются поля «Номер последовательности» (sequence number) и «Номер подтверждения» (acknowledgment number). В качестве номера сегмента используется номер первого передаваемого в нем байта. Если в управляющих сообщениях не передаются байты пользовательской информации, то используется такой порядковый номер, словно сегмент все-таки передает 1 несуществующих байт. В качестве подтверждаемого номера сторона соединения берет порядковый номер последнего успешно принятого байта  и добавляет к нему единицу. Таким образом, подтверждаемый номер — это номер байта, который ожидает сторона соединения. Для каждого направления передачи используется своя нумерация.

Рисунок 2.1 Заголовок TCP-сегмента

Протокол использует три основных алгоритма:

1. Алгоритм коррекции ошибок

2. Алгоритм контроля потока байтов

3. Алгоритм контроля перегрузки

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

Формат TCP-сегмента

Перед данными дописывается заголовок сегмента размером от 20 до 60 байт в зависимости от используемых опций.

Порт – это идентификатор приложения, который определяет обменный буфер, создаваемый операционной системой в оперативной памяти.

Поля «Порт источника» и «Порт адресата» позволяют определить, между какими приложениями на отправителе и получателе организуется TCP-соединение. Поля «Номер последовательности» и «Номер подтверждения» рассмотрены выше.

Поле «Длина заголовка» задает количество 32-битных слов в заголовке, поэтому это поле всегда имеет значения от 5 (5 · 4 = 20 байт) до 15 (15 · 4 = 60 байт). 6 битов зарезервировано для использования в будущем. 6 бит являются управляющими флагами:

URG — используйте информацию из поля «Указатель важности».

ACK — используйте информацию из поля «Номер подтверждения».

PSH —как можно скорее направьте принятые данные приложению.

RST — разорвите TCP-соединение.

SYN — синхронизируйте номера последовательности.

FIN — завершите TCP-соединение.

Одновременно может использоваться несколько флагов.

Поле «Размер окна» указывает на размер окна приема и определяется получателем. Поле «Контрольная сумма» позволяет обнаружить битовые ошибки в пакете и отбросить его в случае необходимости.  Поле «Указатель важности» показывает сколько байтов в начале сегмента должны быть восприняты приложением-получателем как важные. Это поле обозначает количество байтов, которое необходимо добавить к номеру последовательности, чтобы получить номер последнего важного байта в сегменте. Приложение само должно знать как использовать данный вид байтов, так как с точки зрения TCP они ничем не отличаются от остальных. В заголовке могут быть использованы дополнительные поля.

Для доставки сегмента до получателя используется протокол IP (Internet Protocol). Псевдозаголовок IP-дейтаграммы включает в себя: поле «IP-адрес источника», поле «IP-адрес адресата», поле «Протокол» (равно 6 для TCP) и поле «Размер TCP-сегмента».

TCP-соединение

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

Изначально сервер находится в состоянии LISTEN, то есть прослушивает один из портов (рис. 2.2).

TCP-соединение между клиентом и сервером создается в результате тройного рукопожатия:

1. Клиент отправляет на сервер сегмент с установленным флагом SYN (Состояние SYN- SENT).

2. Сервер отвечает сегментом с флагами SYN и ACK (Состояние SYN- RCVD).

3. Клиент отвечает сегментом с флагом ACK (Состояние ESTABLISHED).

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

Рисунок 2.2 Диаграмма установления TCP-соединения на транспортном уровне

После того, как TCP-соединение установлено начинается двусторонний обмен сообщениями. Каждый сегмент несет в поле «Номер подтверждения» номер байта, который он ожидает получить в случае ответной передачи.

TCP-соединение разрывается в результате тройного рукопожатия:

1. Клиент отправляет сегмент с установленными флагами ACK и FIN (Состояние FIN- WAIT-1).

2. Сервер отвечает сегментом с флагами ACK и FIN (Состояние CLOSE- WAIT).

3. Клиент отправляет сегмент с флагом ACK (Состояние TIME- WAIT).

Первые два сегмента могут содержать полезную нагрузку, а последний — нет.

Если третьего шага не произошло, то TCP-соединение оказывается полузакрыто (Состояние FIN-WAIT-2). В этом случае сервер сможет в одностороннем порядке передать данные на клиент, а клиент будет только возвращать подтверждения.

Состояние TIME- WAIT гарантирует то, что клиент ждет некоторое время (≈30-60 секунд) для того, чтобы убедиться, что последний ACK не потерян, либо для того, чтобы открыть то же самое соединение заново, если это необходимо. После периода ожидания TCP-соединение закрывается (состояние CLOSED).

2.3. Реализация TCP на языке C#

В данной ЛР рассматривается учебный пример реализации TCP-сервера и TCP-клиента на языке программирования C#. Язык C# основан на объектно-ориентированной парадигме программирования, основными понятиями которой являются классы и объекты.

Класс — это модель не существующего объекта, описанная на языке программирования.

Методы — процедуры и функции, связанные с классом.

Поля данных — значения, объявленные как принадлежащие классу.

Объект — это сущность в адресном пространстве ПК, появляющаяся при создании экземпляра класса.

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

Язык программирования C# и среда Microsoft Visual Studio Express (VS) позволяют написать код, реализующий TCP-клиент, с помощью класса TcpClient. При таком подходе работа потоковых сокетов оказывается скрытой, а все взаимодействие между сервером и клиентом происходит с помощью методов указанного класса. Именно поэтому в данной ЛР не используется класс Socket. Кроме того, класс TcpClient предоставляет возможность реализации асинхронного процесса приема и передачи информации. Для работы по TCP используются потоковые сокеты. На рис. 2.3 показан алгоритм работы сокетов при TCP-соединении.

Рисунок 2.3 Алгоритм работы сокетов при TCP-соединении

В нашей реализации клиент обладает графическим интерфейсом (GUI). Для взаимодействия с GUI используются события. Событие позволяет классу или объекту (издателю) уведомить другие классы или объекты (подписчики) о возникновении каких-либо ситуаций. В стандартном приложении Windows Forms пользователь подписывается на события, вызываемые элементами управления, такими как кнопки.

TCP-сервер реализуется с помощью классов TcpClient и TcpListener. Сервер ждет подключений новых клиентов на порту, на котором работает экземпляр класса TcpListener. После подключения клиента с помощью асинхронного метода BeginAcceptTcpClient(), дальнейшее взаимодействие между клиентом и сервером осуществляется с помощью экземпляра класса TcpClient. Сервер представляет собой консольное приложение. Он помогает клиентам обмениваться сообщениями друг с другом. Записи о клиентах хранятся в хэш-таблице. Хэш-таблица — это ассоциативный массив, позволяющий хранить, добавлять, удалять и искать ключи и связанные с ними значения.

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

Ход работы

Класс ChatClient

Создание класса ChatClient

Создадим класс, который бы инкапсулировал работу с экземпляром класса TcpClient.

Рисунок 3.2 Добавление нового класса в проект клиентского приложения

Для создания класса необходимо вызвать контекстное меню нажатием правой кнопки мыши на имени проекта client в окне «Окно классов» или «Обозреватель решений», выбрать пункт меню «Добавить» и подпункт «Класс…» (рис. 3.3).

В открывшемся диалоговом окне задайте имя файла, которое соответствует имени нового класса (рис. 3.4). Рекомендуется назвать класс ChatClient. После нажатия кнопки «Добавить» новый класс должен появиться в «Окне классов». Для дальнейшей работы необходимо открыть файл ChatClient.cs двойным щелчком по названию класса в «Обозревателе решений» или «Окне классов». В рабочей области VS возникнет автоматически сгенерированный код для класса ChatClient.

Рисунок 3.3 Диалоговое окно создания класса ChatClient

Цель создания данного класса — инкапсуляция работы с объектом client. Для достижения цели нам потребуется решить три задачи: создать метод для подключения клиента к серверу, метод для передачи и приема сообщений по TCP-протоколу и метод для прекращения работы сервера.

Создание объекта client

Для создания объекта класса TcpClient можно использовать строку кода:

System.Net.Sockets.TcpClient client;

Запись упрощается, если применить директиву using к пространству имен System.Net.Sockets:

TcpClient client;

Поэтому сразу после директивы using System.Threading.Tasks; в блок директив необходимо дописать:

using System.Net.Sockets;

Это позволит вызывать методы для работы с TcpClient в более короткой форме.

Закрытое поле данных client объявляется в файле ChatClient.cs в области видимости ChatClient:

private TcpClient client;

Далее, как закрытые поля данных следует объявить следующие переменные:

private NetworkStream stream;

private byte[] dataWrite;

private byte[] dataRead;

Класс NetworkStream позволяет производить одновременно и передачу, и прием данных. Байтовые массивы dataWrite и dataRead соответственно используются при передаче и приеме данных с помощью асинхронных методов.

VS автоматически проверяет корректность вашего кода. Для получения подробной информации об ошибках наведите курсор на подчёркнутый текст.

Метод Join()

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

public void Join()

{

client = new TcpClient("127.0.0.1", 48654);

stream = client.GetStream();

dataWrite = new byte[client.SendBufferSize];

dataRead = new byte[client.ReceiveBufferSize];

stream.BeginRead(dataRead, 0, client.ReceiveBufferSize, new AsyncCallback(BeginReadCallback), null);

}

С помощью конструктора класса TcpClient создается объект client. Сразу же после создания объект client пытается установить соединение по указанному IP-адресу, например, "127.0.0.1", и порту, например, 48654. Для установления соединения необходимо знать IP-адрес и порт сервера. В случае неудачи приложение завершит работу с ошибкой. Метод GetStream() класса TcpClient позволяет получить доступ к объекту класса NetworkStream через переменную stream. На основании открытых полей данных SendBufferSize и ReceiveBufferSize класса TcpClient создаются байтовые массивы dataRead и dataWrite для приема и передачи информации. Асинхронный метод чтения данных BeginRead() позволяет записать данные в байтовый массив dataRead в отдельном потоке выполнения с помощью метода BeginReadCallback().

Метод BeginReadCallback()

Для обработки полученных сообщений заведем открытое поле данных в классе ChatClient. Для этого запишем области видимости этого класса:

public string recvMessage;

Строковая переменная recvMessage — это последнее принятое сообщение.

Закрытый метод BeginReadCallback() отвечает за обработку принятого сообщения:

private void BeginReadCallback(IAsyncResult ar)

{

int bytesRead = stream.EndRead(ar);

recvMessage = Encoding.UTF8.GetString(dataRead, 0, bytesRead);

stream.BeginRead(dataRead, 0, client.ReceiveBufferSize, new AsyncCallback(BeginReadCallback), null);

}

С помощью метода EndRead() класса NetworkStream в байтовый массив dataRead записывается принятое сообщение. Оно преобразуется в строку lastMessage с помощью метода Encoding.UTF8.GetString(). После обработки сообщения клиент снова запускает метод асинхронного приема данных BeginReadCallback().

Метод Stop()

Работа приложения может быть завершена следующим открытым методом:

public void Stop()

{

stream.Close();

client.Close();

}

При остановке приложения должен быть закрыт поток stream и разорвано TCP-соединение объекта client. 

Таким образом, описанные методы класса ChatClient позволяют инкапсулировать работу с объектом client.

Реализация TCP-клиента

Создание GUI клиента

Перетащите один элемент типа Button, два элемента типа TextBox на форму Form1 и расположите их, как показано на рис. 4.1.

Рисунок 3.4 GUI клиента

Надписи в элементах типа TextBox вводить необязательно.

Событие Click

Используем кнопку button1 для вызова методов Join(). В окне «Обозреватель решений» двойным щелчком по пункту  откроем окно «Конструктор». Двойным щелчком по кнопке button1 автоматически создадим метод, вызываемый в программе при нажатии пользователем этой кнопки. Добавим в тело метода вызов Join():

private void button1_Click(object sender, EventArgs e)

{

cClient.Join();

}

Событие FormClosed

В окне «Конструктор» сделайте активной форму. Нажмите значок  в окне «Свойства» и задайте событие закрытия формы Form1_FormClosed двойным щелчком мыши по пустому полю справа от FormClosed. В открывшемся окне пропишите вызов метода закрытия сокета cClient.Stop() для данного события, если сокет ещё не закрыт:

private void Form1_FormClosed(object sender, FormClosedEventArgs e)

{

cClient.Stop();

}

Событие KeyDown

В окне «Конструктор» сделайте активным элемент textBox2. Нажмите значок  в окне «Свойства» и задайте событие нажатия кнопки textBox2_KeyDown двойным щелчком мыши по пустому полю справа от KeyDown. В открывшемся окне пропишите вызов метода отправки сообщения cClient.Send() для данного события:

private void textBox2_KeyDown(object sender, KeyEventArgs e)

{

if (e.KeyCode == Keys.Return)

{

cClient.Send(textBox2.Text);

textBox2.Clear();

}

}

Сообщение, напечатанное пользователем в поле Text элемента textBox2, будет отправляться только при нажатии клавиши “Enter” в активном элементе textBox2. Оператор ветвления сравнит if код клавиши, нажатой пользователем, с кодом клавиши “Enter”.

Событие MessageExchange

Для того, чтобы GUI программы мог отобразить принятые классом ChatClient сообщения, введем новое событие MessageExchange в данный класс. Для этого создадим открытый делегат в области видимости класса ChatClient в файле ChatClient.cs:

public event EventHandler MessageExchange;

Это событие происходит при приеме или отправке нового сообщения клиентом. В методе BeginReadCallback() после строки recvMessage = Encoding.UTF8.GetString(dataRead, 0, bytesRead); укажите:

MessageExchange(this, EventArgs.Empty);

В методе Send() допишите две строки:

recvMessage = string.Concat("Я> ",sendMessage);

MessageExchange(this, EventArgs.Empty);

Событие произойдет тогда, когда присвоено новое значение строковой переменной recvMessage. Если сообщение написано самим пользователем, то перед ним будет добавлена строка "Я> " с помощью метода string.Concat.

В файле Form1.cs создайте обработчик события cClient_MessageExchange(). Для этого добавьте в области видимости класса новый метод:

private void cClient_MessageExchange(object s, EventArgs e)

{

string newMessage = string.Concat(cClient.recvMessage, "\r\n");

if (!textBox1.InvokeRequired)

textBox1.AppendText(newMessage);

else

textBox1.Invoke(new Action<string>((newMsg) => textBox1.AppendText(newMsg)), newMessage);

}

К сообщению cClient.recvMessage добавляются символы возврата каретки \r и перевода строки \n. Новое сообщение newMessage может быть добавлено к тексту в элементе textBox1. Асинхронная природа требует особого внимания к работе с потоками. Использование оператора ветвления if необходимо, так как метод AppendText() может быть вызван в том же потоке только для сообщений, отправляемых самим клиентом. Для сообщений, принятых от других пользователей, необходимо использовать специальный метод Invoke(), который позволяет обратиться к элементам GUI из другого потока.

Добавьте в конструктор Form1() после InitializeComponent(); строку:

cClient.MessageExchange += cClient_MessageExchange;

Оператор += используется для указания метода, который вызывается в ответ на событие. Такие методы называются обработчиками событий. Использование оператора += в этом контексте называется подпиской на событие.

Проверка работы программы

Запустите клиентское приложение с помощью кнопки . Ошибки, возникшие при выполнении кода, будут показаны в окне «Список ошибок». Если клиентское приложение успешно выполнилось, попробуйте отправить любое сообщение на сервер в локальной сети (IP-адрес и порт узнать у преподавателя). Для этого в качестве IP-адреса клиента используйте его адрес в локальной сети. Сделайте скриншот окна клиентской программы для отчета.

При работе клиентского приложения будет возникать большое количество ошибок. Отметьте в отчете возникшие ошибки выполнения программы. Для того, чтобы эти ошибки не выводились на экран в файле ChatClient.cs необходимо поместить методы BeginRead(), BeginWrite(), EndRead(), EndWrite(), Close() и конструктор TcpClient () в блоки try, например:

try

{

client = new TcpClient("127.0.0.1", 5555);

}

catch (Exception)

{

return;

Добавьте в этот код оператор ветвления if:

if (client == null)

client = new TcpClient("127.0.0.1", 5555);

Как измениться поведение программы?

Опишите в отчете принцип работы TCP-клиента.

Класс ChatServer

Создание класса ChatServer

Создадим класс, который инкапсулирует работу с экземпляром класса TcpListener.

Рисунок 3.6 Добавление нового класса в проект серверного приложения

 

Для создания класса необходимо вызвать контекстное меню нажатием правой кнопки мыши на имени проекта server в окне «Окно классов» или «Обозреватель решений», выбрать пункт меню «Добавить» и подпункт «Класс…» (рис. 3.6).

В открывшемся диалоговом окне задайте имя файла, которое соответствует имени нового класса (рис. 3.7). Рекомендуется назвать класс ChatServer. После нажатия кнопки «Добавить» новый класс должен появиться в «Окне классов». Для дальнейшей работы необходимо открыть файл ChatServer.cs двойным щелчком по названию класса в «Обозревателе решений» или «Окне классов». В рабочей области VS возникнет автоматически сгенерированный код для класса ChatServer.

Рисунок 3.7 Диалоговое окно создания класса ChatServer

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

Создание объекта listener

Для создания экземпляра класса TcpListener можно использовать строку кода:

System.Net.Sockets.TcpListener listener;

Запись упрощается, если применить директиву using к пространству имен System.Net.Sockets:

TcpListener listener;

Поэтому сразу после директивы using System.Threading.Tasks; в блок директив необходимо дописать:

using System.Net.Sockets;

Это позволит вызывать методы для работы с TcpListener в более короткой форме.

Закрытое поле данных listener объявляется в файле ChatServer.cs в области видимости ChatServer:

private TcpListener listener;

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

private AutoResetEvent clientAccepted = new AutoResetEvent(false);

private Hashtable clientTable = new Hashtable();

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

Класс Hashtable позволяет создать хэш-таблицу clientTable, которая будет использоваться для хранения информации о клиентах.

VS автоматически проверяет корректность вашего кода. Для получения подробной информации об ошибках наведите курсор на подчёркнутый текст.

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

Метод Start()

Для запуска сервера будет использоваться следующий метод:

public void Start()

{

listener = new TcpListener(IPAddress.Any, 48654);

listener.Start();

bool isListening = true;

Console.WriteLine("Сервер начал работу.");

Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)

{

listener.Stop();

isListening = false;

Console.WriteLine("Сервер закончил работу.");

};

while (isListening)

{

listener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), listener);

clientAccepted.WaitOne();

}

}

Сервер создается с помощью конструктора класса TcpListener, а IP-адрес IPAddress.Any и порт 5555 подаются в него в качестве аргументов. Метод listener.Start() позволяет запустить работу сервера. Булева переменная isListening определяет текущее состоянии сервера. При получении сигнала о завершении работы (сочетание клавиш Ctrl+C) программа присвоит булевой переменной isListening значение false и вызовет метод Stop(). Если значение isListening равно true, то программа вызывает асинхронный метод обработки клиентских подключений BeginAcceptTcpClient(). После этого вызывается метод WaitOne(), который блокирует текущий поток до получения сигнала от потока обработки нового клиента.

Метод AcceptCallback()

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

public void AcceptCallback(IAsyncResult ar)

{

TcpClient client = listener.EndAcceptTcpClient(ar);

clientAccepted.Set();

IPEndPoint clientAddr = (IPEndPoint) client.Client.RemoteEndPoint;

Console.WriteLine("Подключился клиент {0}", clientAddr.ToString());

clientTable.Add(clientAddr, client);

NetworkStream stream = client.GetStream();

byte[] dataRead = new byte[client.ReceiveBufferSize];

stream.BeginRead(dataRead, 0, client.ReceiveBufferSize, new AsyncCallback(BeginReadCallback), new MyAsyncInfo(dataRead, clientAddr));

}

В результате работы метода EndAcceptTcpClient() класса TcpListener содается экземпляр класса TcpClient. Связь с каждым клиентом поддерживается с помощью отдельного потокового сокета и отдельного объекта client. Объект clientAccepted с помощью метода Set() отправляет сигнал о продолжении работы в метод Start(). Тем временем, в данном потоке продолжается работа с текущим клиентом. В объект clientAddr класса IPEndPoint записывается IP-адрес и портэтого клиента. Данный объект будет выполнять роль ключа в хэш-таблице clientTable. С помощью метода Add() в таблицу добавляется объект client и соответствующий ему ключ clientAddr. Объект stream позволяет обмениваться информацией с текущим клиентом. Байтовый массив dataRead используется для приема данных и имеет размер равный client.ReceiveBufferSize. С помощью метода BeginRead() осуществляется асинхронный прием данных от текущего клиента. Для дальнейшей обработки полученной информации в вызываемый метод BeginReadCallback() необходимо подать указатель на массив с полученной информацией dataRead и ключ для хэш-таблицы clientAddr. Для этого используется подкласс:

public class MyAsyncInfo

{

public Byte[] Data { get; set; }

public IPEndPoint Address { get; set; }

 

public MyAsyncInfo(Byte[] data, IPEndPoint address)

{

Data = data;

Address = address;

}

}

Этот подкласс играет роль структуры и инкапсулирует байтовый массив Data и экземпляр класса IPEndPoint в один объект класса MyAsyncInfo. Ключевые слова get и set описывают стандартные методы работы с полями данных этого класса. В конструкторе MyAsyncInfo() полям присваиваются начальные значения data и address. Подкласс должен быть объявлен в области видимости класса ChatServer.

Метод BeginReadCallback()

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

public void BeginReadCallback(IAsyncResult ar)

{

MyAsyncInfo info = ar.AsyncState as MyAsyncInfo;

IPEndPoint clientAddr = info.Address;

byte[] dataRead = info.Data;

TcpClient client = (TcpClient)clientTable[clientAddr];

NetworkStream stream = client.GetStream();

int bytesRead = stream.EndRead(ar);

if (bytesRead < 1)

{

stream.Close();

client.Close();

clientTable.Remove(clientAddr);

Console.WriteLine("Отключился клиент {0}", clientAddr.ToString());

}

else

{

string recvMessage = Encoding.UTF8.GetString(dataRead, 0, bytesRead);

Broadcast(recvMessage);

stream.BeginRead(dataRead, 0, client.ReceiveBufferSize, new AsyncCallback(BeginReadCallback), new MyAsyncInfo(dataRead, clientAddr));

}

}

В объекты clientAddr и dataRead записывается информация, полученная в виде экземпляра класса MyAsyncInfo. По ключу clientAddr из хэш-таблицы clientTable извлекается объект client класса TcpClient. Объект stream позволяет обмениваться данными с текущим клиентом client. С помощью метода EndRead() данные, полученные от клиента, записываются в байтовый массив dataRead. Если было получено сообщение о разрыве соединения, то работа с объектами stream и client заканчивается с помощью соответствующих методов, а объект, соответствующий данному клиенту, удаляется из хэш-таблицы с помощью метода Remove() по ключу clientAddr. В противоположном случае, байтовый массив преобразуется в строковую переменную recvMessage с помощью метода Encoding.UTF8.GetString(). Строковая переменная recvMessage передается в метод Broadcast() в качестве аргумента. Работа с текущим продолжается с помощью асинхронного метода BeginRead().

Метод Broadcast()

Любое сообщение может быть доставлено до всех клиентов, записи о которых существуют в хэш-таблице clientTable, с помощью следующего метода:

private void Broadcast(string sendMessage)

{

foreach (DictionaryEntry entry in clientTable)

{

TcpClient client = (TcpClient)(entry.Value);

NetworkStream stream = client.GetStream();

byte[] dataSend = new byte[client.SendBufferSize];

dataSend = Encoding.UTF8.GetBytes(sendMessage);

stream.BeginWrite(dataSend, 0, dataSend.Length, new AsyncCallback(BeginWriteCallback), stream);

}

}

С помощью оператора цикла foreach осуществляется перебор всех записей entry класса DictionaryEntry в хэш-таблице clientTable. Каждая запись представляет собой объект client класса TcpClient. Объект stream позволяет обмениваться информацией с текущим клиентом. Создается байтовый массив dataSend, в который записывается строковая переменная sendMessage, преобразованная методом Encoding.UTF8.GetBytes(). Процедура записи осуществляется в асинхронном режиме методом BeginWrite(). В другой поток передается только объект stream.

Метод BeginWriteCallback()

При отправке сообщения клиенту вызывается следующий метод:

private void BeginWriteCallback(IAsyncResult ar)

{

NetworkStream stream = (NetworkStream)ar.AsyncState;

stream.EndWrite(ar);

}

Проверка работы программы

Запустите сервер с помощью кнопки . В качестве IP-адреса используйте IP-адрес вашего ПК в локальной сети (определите его с помощью команды ipconfig). В качестве порта используйте значение 48654 + номер_варианта, номера портов указаны в Приложении A.Ошибки, возникшие при выполнении кода, будут показаны в окне «Список ошибок». Если сервер успешно начал работу, попробуйте подключиться к нему с помощью ранее созданного клиентского приложения (exe-файл можно найти в client/bin/debug/). Запустите еще одну копию клиентского приложения и проведите передачу нескольких сообщений. С помощью оператора ветвления if и метода Broadcast() сделайте так, чтобы сервер рассылал сообщения всем кроме отправителя. Проверьте работу сервера и клиентов соответственно вашему варианту, указанному в Приложении A. Добавьте новый функционал в работу сервера согласно вашему варианту с помощью метода Broadcast(). Проведите обмен сообщениями между сервером и количеством клиентом, указанным в Приложении A. Сделайте скриншот окна клиента после обмена сообщениями с сервером.

Опишите в отчете принцип работы TCP-сервера.

Выводы

Сделайте выводы по проделанной работе. С помощью какого класса был реализован TCP-клиент в данной ЛР? Опишите задачи, которые необходимо решить для создания простейшего клиента. С помощью каких классов был реализован TCP-сервер? Опишите задачи, которые необходимо решить при создании сервера. Каким образом осуществляется взаимодействие между клиентом и сервером по протоколу TCP? Какие ошибки могут произойти в данных приложениях и чем они могут быть вызваны?

Вопросы

· Что такое TCP-сервер?

· Что такое TCP-клиент?

· На каком уровне модели OSI происходит взаимодействие по TCP-протоколу?

· Каким образом был реализован TCP-сервер на языке C# в данной ЛР?

· Можно ли реализовать сервер на языке C# как-то иначе?

· Какие ошибки могут возникнуть при работе по протоколу TCP?

· В чем различие между клиентом и сервером, работающими по протоколу TCP?

· В чем главная особенность протокола TCP?


·

Приложение A

Варианты

Номер варианта Изменение функционала сервера Номер порта Количество клиентов
1 Сервер сообщает о новом клиенте всем остальным 48655 2
2 Сервер сообщает об отключении клиента всем остальным 48656 2
3 Сервер приветствует каждого клиента 48657 2
4 Сервер возвращает текущее время новому клиенту 48658 2
5 Сервер возвращает текущую дату новому клиенту 48659 2
6 Сервер сообщает о новом клиенте всем остальным 48660 3
7 Сервер сообщает об отключении клиента всем остальным 48661 3
8 Сервер приветствует каждого клиента 48662 3
9 Сервер возвращает клиенту текущее время новому клиенту 48663 3
10 Сервер возвращает клиенту текущую дату новому клиенту 48664 3
11 Сервер сообщает о новом клиенте всем остальным 48665 4
12 Сервер сообщает об отключении клиента всем остальным 48666 4
13 Сервер приветствует каждого клиента 48667 4
14 Сервер возвращает клиенту текущее время новому клиенту 48668 4
15 Сервер возвращает клиенту текущую дату новому клиенту 48669 4
16 Сервер сообщает о новом клиенте всем остальным 48670 5
17 Сервер сообщает об отключении клиента всем остальным 48671 5
18 Сервер приветствует каждого клиента 48672 5
19 Сервер возвращает клиенту текущее время новому клиенту 48673 5
20 Сервер возвращает клиенту текущую дату новому клиенту 48674 5

 

Постановка задачи

Используя данные методические указания, создать два сетевых приложения в среде Microsoft Visual Studio Express 2013. По результатам ЛР должно быть создано два проекта на языке C#. Клиент должен быть реализован в виде приложения Windows Forms и должен поддерживать прием и передачу текстовых сообщений по протоколу TCP. Сервер должен быть реализован в виде консольного приложения и должен обеспечивать обмен сообщениями между клиентами по протоколу TCP. Протокол TCP должен быть реализован с использованием классов TcpListener и TcpClient, а приложения должны работать в асинхронном режиме.

В отчете представляются: название работы, постановка задачи, результаты, скриншоты программы и вывод.

2. Краткая теоретическая справка

Системы мгновенного обмена сообщениями

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

Мессенджер — это клиентское приложение, используемое для обмена сообщениями.

Обычно клиенты подключаются к серверу сети обмена сообщениями. Популярностью пользуются такие мессенджеры, как IRC, AIM, ICQ, MSN, XMPP. Между различными мессенджерами обычно нет прямой связи (исключение — XMPP). В ЛР будет реализован сервер и клиент для обмена мгновенными сообщениями по протоколу TCP.

Транспортный протокол TCP

На транспортном уровне часто используется протокол TCP (Transmission Control Protocol). Этот протокол является важнейшим элементом стека протоколов TCP/IP. Существует разные реализаций данного протокола, например, TCP Tahoe, TCP Reno, TCP Vegas и проч. Алгоритмы, используемые в его работе, много раз усовершенствовались.  TCP применяется для гарантированной доставки сообщений других протоколов, например, FTP, TELNET, SMTP, DNS, HTTP.

В протоколе TCP передаваемая информация рассматривается как поток байтов. Он передается по «трубе», соединяющей два сетевых приложения. Поток разделен на сегменты.

Сегмент — это набор байтов, сформированный протоколом TCP.

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


Поделиться с друзьями:

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

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...

Особенности сооружения опор в сложных условиях: Сооружение ВЛ в районах с суровыми климатическими и тяжелыми геологическими условиями...

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



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.25 с.