Веб-приложения реального времени. Веб-сокеты, IIS 8, библиотека SignalR и их использование в приложениях ASP.NET. Часть третья, простой пример использования веб-сокетов в приложении ASP.NET.

В первой статье был описан протокол WebSocket, а во второй процесс установки IIS 8 в операционной системе Windows 8. В данной статье будет показан простой пример использования веб-сокетов в ASP.NET. Для того, чтобы иметь возможность выполнить пример из статьи у себя, нужна будет машина с установленной Windows 8 или Windows Server 2012 с установленными IIS 8 и Visual Studio 2012.  Создаём пустое приложение Web Forms 4.5 в Visual Studio.



После того как проект создан. Добавим в него пустую веб-форму, после следующее содержимое.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm.aspx.cs"
  Inherits="SimpleWeSocketbApplication.WebForm" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title></title>
</head>
<>
  <form id="form1" runat="server">
    <div>
      <span id="webSocketStatusSpan"></span>
      <br />
      <span id="webSocketReceiveDataSpan"></span>
      <br />
      <span>Введите строку для отправки</span>
      <br />
      <input id="nameTextBox" type="text" value="" />
      <input type="button" value="Отправить данные" onclick="SendData();" />
      <input type="button" value="Закрыть веб-сокет" onclick="CloseWebSocket();" />
    </div>
  </form>
  <script type="text/javascript">
 
    var webSocketStatusSpan = document.getElementById("webSocketStatusSpan");
    var webSocketReceiveDataSpan = document.getElementById("webSocketReceiveDataSpan");
    var nameTextBox = document.getElementById("nameTextBox");
 
    var webSocket;
    //Адрес нашего обработчика HTTP-данных, который будет отвечать на запросы.
    var handlerUrl = "ws://localhost/SimpleWebSocketApplication/WebSocketHandler.ashx";
 
    //Метод отправляющий данные.
    function SendData() {
      //Метод инициализирующий веб-сокет.
      InitWebSocket();
      //Смотрим, если веб-сокет открыт и готов к использованию
      //отправляем данные.
      if (webSocket.OPEN && webSocket.readyState == 1)
        webSocket.send(nameTextBox.value);
      //Если веб-сокет закрывается или закрыт, выводим сообщение.
      if (webSocket.readyState == 2 || webSocket.readyState == 3)
        webSocketStatusSpan.innerText = "Веб-сокет закрыт, отправить данные невозможно."
    }
    function CloseWebSocket() {
      //Функция для программного закрытия веб-сокета.
      //После закрытия, получать или отправлять данные не получится.
      webSocket.close();
    }
    function InitWebSocket() {
      //Если объект веб-сокета не инициализирован, инициализируем его.
      if (webSocket == undefined) {
        webSocket = new WebSocket(handlerUrl);
 
        //Устанавливаем обработчик открытия соединения.
        webSocket.onopen = function () {
          webSocketStatusSpan.innerText = "Веб-сокет окрыт."
          webSocket.send(nameTextBox.value);
        };
 
        //Устанавливаем обработчик получения данных.
        webSocket.onmessage = function (e) {
          webSocketReceiveDataSpan.innerText = e.data;
        };
 
        //Устанавливаем обработчик закрытия соединения.
        webSocket.onclose = function () {
          webSocketStatusSpan.innerText = "Веб-сокет закрыт."
        };
 
        //Устанавливаем обработчик ошибки.
        webSocket.onerror = function (e) {
          webSocketStatusSpan.innerText = e.message;
        }
      }
    }
 
  </script>
</>
</html>
Конечной точкой, обрабатывающей запрос по протоколу WebSocket, на стороне сервера будет обработчик HTTP-данных. Добавим его в наше приложение, а после следующий код.
using System;
using System.Web;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
 
using System.Net.WebSockets;
using System.Web.WebSockets;
 
namespace SimpleWeSocketbApplication
{
  public class WebSocketHandler : IHttpHandler
  {
    public void ProcessRequest(HttpContext context)
    {
      //Проверяем, является ли запрос, запросом по протоколу WebSocket.
      if (context.IsWebSocketRequest)
      {
        //Если да, назначаем асинхронный обработчик.
        context.AcceptWebSocketRequest(WebSocketRequestHandler);
      }
    }
 
    public bool IsReusable { get { return false; } }
 
    //Асинхронный обработчик запроса.
    public async Task WebSocketRequestHandler(AspNetWebSocketContext webSocketContext)
    {
      //Получаем текущий объект веб-сокета.
      WebSocket webSocket = webSocketContext.WebSocket;
 
      /*Определяем некую константу, которая будет представлять
      максимльный размер входных данных. Её устанавливаем мы и значение
      можем задать любым. Мы знаем, что в данном случае размер пересылаемых
      данных очень мал.
      */
      const int maxMessageSize = 1024;
 
      //Буфер битов, в который будут записываться полученные данные.
      ArraySegment<Byte> receivedDataBuffer = new ArraySegment<Byte>(new Byte[maxMessageSize]);
 
      //Токен отмены, в данном примере не используется.
      var cancellationToken = new CancellationToken();
 
      //Проверяем состояние веб-сокета.
      while (webSocket.State == WebSocketState.Open)
      {
        //Читаем данные.
        WebSocketReceiveResult webSocketReceiveResult = 
          await webSocket.ReceiveAsync(receivedDataBuffer, cancellationToken);
        //Смотрим, если входной фрейм закрывающий, посылаем ответ на закрытие.
        if (webSocketReceiveResult.MessageType == WebSocketMessageType.Close)
        {
          await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, 
            String.Empty, cancellationToken);
        }
        else
        {
          //Читаем только те байты, которые пришли.
          byte[] payloadData = receivedDataBuffer.Array.Where(b => b != 0).ToArray();
          //Поскольку мы знаем, что это строка, то конвертируем.
          string receiveString = 
            System.Text.UTF8Encoding.UTF8.GetString(payloadData, 0, payloadData.Length);
          //Собираем новую строку и преобразовываем в массив байт.
          var newString = 
            String.Format("Hello, " + receiveString + " ! Time {0}", DateTime.Now.ToString());
          Byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes(newString);
          //Отсылаем данные обратно браузеру.
          await webSocket.SendAsync(new ArraySegment<byte>(bytes),
            WebSocketMessageType.Text, true, cancellationToken);
        }
      }
    }
  }
}
После того как эти два файла добавлены в проект, устанавливаем локальный IIS 8 в качестве сервера по умолчанию. Хотя использовать IIS 8 Express тоже можно.



Компилируем и запускаем приложение. После того как откроется IE 10, нажимем кнопку "Отправить данные" предварительно введя какое-нибудь слово. Получится примерно следующее:



К сожалению в настоящий момент, кроме Google Chrome, ни один из браузеров не отображет трафик по протоколу WebSocket, в том числе и мой любимый Firefox. Но, думаю, что эта ситуация исправится в близжайшем будущем.



Всё, что было показано выше, демонстрирует работу с протоколом WebSocket в ASP.NET на довольно низком уровне. Специально для этого в сборку System.Web было добавлено новое пространство имён – System.Web.WebSockets. В следующей статье будет показан более сложный пример показывающий истинные возможности веб-сокетов и демонстрирующий работу с их использованием. Исходный код примеров можно скачать отсюда.
wUI
19.08.2013 14:47
Спасибо за Ваш труд в этом цикле статей. =)
19.08.2013 15:19
Спасибо!
evolcoder
31.03.2014 19:45
по моему websocket так и остался не став популярным. Тем более что Asp.NET WebForms просто ужасная технология.
31.03.2014 19:53
Кто вам сказал? Наоборот, это будущее веба. У нас есть проекты, где веб-сокеты это уже стандарт. Технология WebForms сложная сама по себе и предназначена для генерации преимущественно статических данных. Веб развивается бурными темпами, страницы становятся всё интерактивней и уже нужны другие технологии.