Главная » Silverlight » Сервер сообщений

0

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

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

Главное отличие состоит в том, что сервер сообщений выполняет прослушивание че­рез разные порты. Как упомянуто выше, Silverlight разрешает сокетному приложению использовать любой порт в диапазоне от 2504 до 4532. Например, следующий объект прослушивания использует порт 4530.

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

Когда сервер сообщений получает запрос на соединение, он выполняет одну допол­нительную операцию. Как и сервер политики, он создает экземпляр класса (в данном примере MessengerConnection) для обслуживания коммуникации. Однако, кроме это­го, он добавляет клиентский объект в коллекцию, отслеживающую всех подключенных пользователей. Это единственный способ обеспечить взаимодействие между клиентами, передавая сообщения от одного пользователя к другому. Ниже приведены коллекция, выполняющая отслеживание, и поле, с помощью которого сервер присваивает каждому новому клиенту уникальный идентификационный номер.

private int clientNum;

private List<MessengerConnection> clients = new List<MessengerConnection> ();

Когда клиент устанавливает соединение, приведенный ниже код создает объект MessengerConnection и добавляет клиентский объект в коллекцию.

clientNum++;

Console.WriteLine("Клиент #" + clientNum.ToString() + " подключен.");

// Создание нового объекта MessengerConnection clientHandler = new MessengerConnection(client, "Client " + clientNum.ToString(), this); clientHandler.Start();

lock (clients) {

clients.Add(clientHandler);

}

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

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

foreach (MessengerConnection client in clients) {

client.Close(); } ‘

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

Для реализации обмена сообщениями необходимы два компонента. Во-первых, нуж­но обработать предоставление сообщения в классе MessengerConnection. Во-вторых, нужно обработать отправку сообщения в классе MessengerServer.

Когда объект MessengerConnection создан и его метод Start () вызван, начинается прослушивание.

public void Start () {

try

(

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

client.Client.BeginReceive(message, 0, message.Length, SocketFlags.None, new AsyncCallback(OnDataReceived), null);

}

catch (SocketException se)

{

Console.WriteLine(se.Message) ;

}

)

Когда клиент передает данные, запускается метод обратного вызова OnData­Received () . Он читает данные по одному байту, пока не получит всю информацию, переданную клиентом. Затем он передает данные методу MessengerServer . Deliver () и включает новое прослушивание.

public void OnDataReceived(IAsyncResult asyn) {

try

{

int bytesRead = client.Client.EndReceive(asyn);

if (bytesRead > 0) {

// Приказ серверу передать сообщение всем клиентам server.DeliverMessage(message, bytesRead);

// Включение прослушивания

client.Client.BeginReceive(message, 0, message.Length, SocketFlags.None, new AsyncCallback(OnDataReceived), null);

}

}

catch (Exception err) {

Console.WriteLine(err.Message);

}

}

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

Метод MessengerServer .DeliverMessage О проходит по коллекции клиентов и вы­зывает через каждого клиента метод ReceiveMessage () для передачи сообщения даль­ше. При этом возникают проблемы многопоточности. Блокировка всей коллекции — не идеальное решение, потому что процесс отправки занимает некоторое время, особенно если клиент не отвечает. Чтобы избежать замедления, метод DeliverMessage () создает текущую копию коллекции, а затем применяет ее для отправки сообщения.

public void DeliverMessage(byte[) message, int bytesRead)

{

Console.WriteLine("Отправка сообщения.");

// Дублирование коллекции MessengerConnection[] connectedClients;

lock (clients) {

connectedClients = clients.ToArray();

}

foreach (MessengerConnection client in connectedClients) {

try {

client.ReceiveMessage(message, bytesRead);

}

catch {

// Клиент отключен;

// удаление клиентского объекта из коллекции lock (clients) (

clients.Remove(client);

client.Close();

}

}

}

Метод MessengerConnection. ReceiveMessage () записывает сообщение обратно в по­ток, чтобы клиент мог получить его.

public void ReceiveMessage(byte [ ] data, int bytesRead)

{

client. GetStreamO .Write (data, 0, bytesRead);

}

И наконец, необходимо изменить код метода Main () таким образом, чтобы приложе­ние создавало и запускало оба сервера (сообщений и политики). В приведенном ниже коде добавления отмечены полужирным шрифтом.

static void Main(string[] args)

{

PolicyServer policyServer = new PolicyServer("clientaccesspolicy.xml"); policyServer.Start ();

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

MessengerServer messenger Server = new MessengerServer () ; messengerServer.Start{);

Console.WriteLine(" запущен.");

Console.WriteLine("Нажмите клавишу Enter аёу Sfloiaa.");

// Ожидание нажатия клавиши <Enter>; можно также

// задать ожидание определенной строки (например, quit)

// или нажатия любой клавиши

//с помощью метода Console. ReadKeyO

Console.ReadLine();

policyServer.Stop ();

Console.WriteLine("Сервер политики закрыт."); messengerServer.Stop();

Console.WriteLine (" закрыт.") ;

На рис. 20.8 (см. выше) показано, что происходит на экранах, когда два клиента начинают разговор посредством сокетного сервера. На рис. 20.9 показана серверная часть этого же процесса: сообщения, выводимые на консоли сокетного сервера, когда клиенты подключаются и взаимодействуют.

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии