Главная » Silverlight » Клиентское приложение

0

До сих пор наше внимание было сосредоточено исключительно на серверном при­ложении .NET, обрабатывающем сообщения на сервере. Это наиболее сложная часть ре­шения, однако сокетное клиентское приложение Silverlight тоже содержит важный код.

решает три важные задачи: установка соединения с серве­ром; передача сообщений; прием и вывод сообщений. Коды клиентского и серверного приложений похожи, однако создание клиентского приложения требует немного боль­ше ручной работы. Это объясняется тем, что в Silverlight нет класса TcpClient. Вместо него используется более низкоуровневый класс Socket.

Рис. 20.9. Сообщения серверов сообщений и политики

В классе Socket используются три асинхронных метода: ConnectAsync () для создания соединения, SendAsyncO для передачи сообщения и ReceiveAsync () для прослушивания порта. Каждому из них необходимо предоставить объект типа SocketAsyncEventArgs.

Объект SocketAsyncEventArgs решает две важные задачи.

•       Служит пакетом, содержащим любые дополнительные данные, которые нужно передать.

•      Извещает о завершении асинхронной операции с помощью события Completed.

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

Установка соединения с сервером

Первая задача клиентского приложения состоит в установке соединения, когда поль­зователь щелкает на кнопке cmdConnect. Для этого клиентское приложение должно соз­дать объекты Socket и SocketAsyncEventArgs.

// Сокет для нижележащего соединения, private Socket socket;

private void cmdConnect_Click(object sender, RoutedEventArgs e)

{

try {

if ((socket != null) && (socket.Connected == true)) socket.Close () ;

}

catch (Exception err)

і

AddMessage("ОШИБКА: " + err.Message);

}

DnsEndPoint endPoint =

new DnsEndPoint(Application.Current.Host.Source. DnsSafeHost, 4530); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

SocketAsyncEventArgs args = new SocketAsyncEventArgs();

// Для конфигурирования объекта SocketAsyncEventArgs нужно

// установить объект Socket в свойство UserToken и адрес // отдаленного сервера в свойство RemoteEndPoint args.UserToken = socket; args.RemoteEndPoint = endPoint;

// Подключение обработчика Completed args.Completed +=

new EventHandler<SocketAsyncEventArgs> (OnSocketConnectCompleted);

// Запуск процесса асинхронного соединения socket.ConnectAsync(args);

}

Большинство операций довольно простые. Если сокет в данный момент открыт, он закрывается. Затем код создает объект DnsEndPoint для идентификации расположения отдаленного узла. В данном случае отдаленным узлом является веб-сервер, предостав­ляющий страницу Silverlight. Используется порт 4530. И наконец, код создает объект типа SocketAsyncEventArgs и подключает обработчик OnSocketConnectCompleted () к событию Completed.

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

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

private void AddMessage(string message) {

Dispatcher.Beginlnvoke (

delegate() {

lblMessages.Text-+= message + "\n"; // Прокрутка в конец списка, // чтобы сообщение было видно ScrollViewer.ScrollToVerticalOffset ( ScrollViewer.ScrollableHeight); });

}

Когда клиентское приложение пытается завершить соединение, запускается обра­ботчик OnSocketConnectCompleted (). Он обновляет и наново конфигурирует объект SocketAsyncEventArgs, подключая событие Completed к новому обработчику, а затем начинает прослушивание.

private void OnSocketConnectCompleted(object sender, SocketAsyncEventArgs e)

{

if (!socket.Connected) {

AddMessage("Установить соединение не удалось."); return;

}

AddMessage("Соединение с сервером установлено.");

II Длина сообщения не должна превышать 1024 байт byte[] response = new byte[1024]; e.SetBuffer(response, 0, response.Length); e.Completed -=

new EventHandler<SocketAsyncEventArgs>

(OnSocketConnectCompleted); e.Completed += new EventHandler<SocketAsyncEventArgs> (OnSocketReceive);

II Установка прослушивания socket.ReceiveAsync(e) ;

}                                     V

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

Передача сообщений

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

public class Message

(

public string MessageText {get; set;) public string Sender {get; set;) public DateTime SendTime {get; set;}

public Message (string messageT.ext, string sender) {

MessageText = messageText;

Sender = sender;

SendTime = DateTime.Now;

)

// Конструктор без аргументов, делающий экземпляр класса // доступным для сериализации public Message() { }

}

Чтобы передать сообщение, пользователь вводит текст и щелкает на кнопке cmd- Send. В этот момент создается новый объект-типа SocketAsyncEventArgs. Первый объ­ект все еще активен и ожидает новых сообщений в фоновом потоке. Новый объект SocketAsyncEventArgs должен сохранить буфер данных сообщения. Необходимо запу­стить конструктор объекта Message, сериализовать объект сообщения в поток с помо­щью объекта XmlSerializer, преобразовать поток в массив байтов и добавить его в объ­ект SocketAsyncEventArgs с помощью свойства Buf ferList.

private void cmdSend_Click(object sender, RoutedEventArgs e)

{

if ((socket == null) I I (socket.Connected — false)) {

AddMessage("ОШИБКА: нет соединения.");

return;

SocketAsyncEventArgs args = new SocketAsyncEventArgs();

// Подготовка сообщения XmlSerializer serializer = new XmlSerializer(typeof(Message)); MemoryStream ms = new MemoryStream(); serializer.Serialize(ms, new Message(txtMessage.Text, txtName.Text)); byte[] messageData = ms.ToArray(); List<ArraySegment<byte>> bufferList = new List<ArraySegment<byte>>(); bufferList.Add(new ArraySegment<byte>(messageData)); args.BufferList = bufferList;

II Передача сообщения socket.SendAsync(args);

}

К сожалению, класс Socket платформы Silverlight более низкоуровневый, чем класс TcpClient платформы .NET, поэтому, в отличие от приложения на стороне сервера, в Silverlight не существует простого способа организации потокового доступа к сетево­му соединению.

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

Получение сообщений

Когда сообщение передано клиенту, другой объект типа SocketAsyncEventArgs ге­нерирует свое событие Completed, которое запускает обработчик OnSocketReceive О . В этот момент нужно десериализовать сообщение, вывести его на экран и установить ожидание следующего сообщения.

private void OnSocketReceive(object sender, SocketAsyncEventArgs e)

{

if (e.BytesTransferred == 0) {

AddMessage("Нет соединения с сервером.");

try {

socket.Close();

}

catch { ) return;

}

try {

// Получение и вывод сообщения XmlSerializer serializer = new XmlSerializer(typeof(Message)); MemoryStream ms = new MemoryStream(); ms .Write(e.Buffer, 0, e.BytesTransferred) ; ms.Position = 0;

Message message = (Message)serializer.Deserialize(ms);

AddMessage ("[" + message.Sender + "] " + message.MessageText +

" ( " + message.SendTime.ToLongTimeStringO +"")");

}

catch (Exception err) {

AddMessage("ОШИБКА: " + err.Message) ;

}

// Установка прослушивания socket.ReceiveAsync(e) ;

}

Этим завершается создание клиентской части приложения. Загрузите код для при­меров главы и поэкспериментируйте с клиентским и серверным приложениями чата.

Примечание. Существует много способов усовершенствования рассмотренного чатового приложения. На стороне сервера можно заменить механизм передачи сообщений более сложной логикой, позволяющей распознавать разные типы сообщений и выполнять над ними разные операции. Можно добавить средства управления пользовательским интерфейсом (например, отключение элементов на основе наличия активного соединения). Можно также добавить передачу на сервер извещения о закрытии соединения, разрешить передачу сообщения определенным пользователям, добавить аутентификацию пользователей, информировать вновь подключаемых пользователей о текущем количестве пользователей в чате и т.д. Рассмотренное сокетное приложение — лишь демонстрация возможностей, предоставляемых платформой Silverlight для организации низкоуровневых средств сетевой коммуникации.

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

По теме:

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