Persistent Connection API

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 31.10.2015

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

Создадим проект ASP.NET MVC 4 по типу Basic. Назовем его, к примеру, PersistentConMvc. Затем через менеджер NuGet добавим в проект библиотеку SignalR.

Добавим стандартный контроллер в папку Controllers. Пусть это будет HomeController с одним действием Index:

using System;
using System.Web;
using System.Web.Mvc;

namespace PersistentConMvc.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

Затем добавим в папку Models модель Data. Эта модель будет представлять объект передаваемого сообщения. В принципе мы могли бы обмениваться простыми строковыми объектами. Однако использование более сложных объектов позволит нам структурировать передаваемые данные, легче ими управлять. Итак, модель Data будет иметь следующий код:

using System;

namespace PersistentConMvc.Models
{
    public class Data
    {
        public string Name { get; set; } // Имя пользователя
        public string Message { get; set; } // Сообщение пользователя
    }
}

Этот объект будет передаваться от пользователя серверу.

Теперь создадим серверную инфраструктуру для взаимодействия с клиентом. Создадим в проекте папку Connection и добавим в нее новый класс ChatConnection:

using System;
using System.Collections.Generic;
using System.Web;
using Microsoft.AspNet.SignalR;
using PersistentConMvc.Models;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace PersistentConMvc.Connection
{
    public class ChatConnection : PersistentConnection
    {
        protected override Task OnConnected(IRequest request, string connectionId)
        {
            Data chatData = new Data() { Name = "Сообщение сервера", Message = "Пользователь "+connectionId+" присоединился к чату" };
            return Connection.Broadcast(chatData);
        }

        protected override Task OnReceived(IRequest request, string connectionId, string data)
        {
            Data chatData = JsonConvert.DeserializeObject<Data>(data);
            return Connection.Broadcast(chatData);
        }

        protected override Task OnDisconnected(IRequest request, string connectionId)
        {
            Data chatData = new Data() { Name = "Сообщение сервера", Message = "Пользователь " + connectionId + " покинул чат" };
            return Connection.Broadcast(chatData);
        }
    }
}

Класс ChatConnection наследуется от класса PersistentConnection и переопределяет некоторые его методы.

Метод OnConnected срабатывает при каждом подключении клиента. В него передаются два параметра: IRequest request, который представляет объект запроса, и connectionId, который представляет id, автоматически присваемый обратившемуся к серверу клиенту.

Здесь мы просто посылаем клиенту в методе Connection.Broadcast объект Data. Метод Broadcast может принимать любой объект, в том числе и простую строку, главное, чтобы в коде клиента мы могли распарсить должным образом это сообщение.

Метод OnDisconnected аналогичен методу OnConnected, только выполняется при отключении клиента.

При получении сообщения от клиента срабатывает метод OnReceived: полученное сообщение передается в третьем параметре data. Поскольку это сообщение представляет строку, мы его десериализуем в объект Data и затем рассылаем его всем подключенным клиентам.

Для того, чтобы подключение и использование Persistent Connection API было доступно, зарегистрируем его в файле Global.asax в методе Application_Start:

protected void Application_Start()
{

    RouteTable.Routes.MapConnection<ChatConnection>("chat", "/chat");

    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Строка RouteTable.Routes.MapConnection<ChatConnection>("chat", "/chat"); регистрирует маршрут, по которому мы будем обращаться из клиентской части.

Теперь клиентская часть. Создадим для метода Index контроллера Home представление Index.cshtml:

@{
    ViewBag.Title = "Чат";
}

<h2>Чат</h2>

    <div id="loginBlock">
         Введите логин:<br />
         <input id="txtUserName" type="text" /><br />
         <input id="btnLogin" type="button" value="Войти" />
    </div>
    <div id="chatBody">
        <div id="header"></div>
        <div id="inputForm">
            <input type="text" id="message" />
            <input type="button" id="sendmessage" value="Отправить" />
        </div>
        <div id="chatroom">
            <ul></ul>
        </div>
        
    </div>
<input id="username" type="hidden" />

@section scripts {
    <!--Ссылка на библиотеку SignalR -->
    <script src="~/Scripts/jquery.signalR-1.1.3.min.js"></script>
    <script>
    $(function () {

        $('#chatBody').hide();
        // получаем соединение
        var myConnection = $.connection("/chat");
        // обработка получения данных от сервера
        myConnection.received(function (data) {
            $("#chatroom ul").append("<li><strong>" + htmlEncode(data.Name) +
                '</strong>: ' + htmlEncode(data.Message) + "</li>");
        });
        
            // обработка логина
            $("#btnLogin").click(function () {

                var userName = $("#txtUserName").val().replace(/\s/g, '');
                if (userName.length > 0) {
                    $('#username').val(userName);

                    //отключаем поля ввода логина
                    $('#btnLogin').attr('disabled', 'disabled');
                    $('#txtUserName').attr('disabled', 'disabled');

                    $('#chatBody').show();
                    // открываем соединение
                    myConnection.start().done(function () {
                        //обработчик отправки сообщения
                        $('#sendmessage').click(function () {
                           // посылаем сериализованный объект на сервер
                            myConnection.send(JSON.stringify({ name: $('#username').val(), message: $('#message').val() }));
                            $('#message').val('');
                        });
                    }); 
                }
                else {
                    alert("Введите имя");
                }
            }); 
     });
    // Кодирование тегов
    function htmlEncode(value) {
        var encodedValue = $('<div />').text(value).html();
        return encodedValue;
    }
    </script>
}

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

В коде javascript первым делом мы создаем подключение: var myConnection = $.connection("/chat");. В качестве параметра указывается тот маршрут, который мы выше зарегистрировали в файле Global.asax.

Функция myConnection.received вызывается при получении клиентом от сервера данных. В данном случае поскольку сервер передает клиенту объект Data, у которого есть свойства Name и Message, то мы можем их получить в данной функции и добавить на страницу в список сообщений.

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

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

Чтобы пользователь не мог второй раз ввести ник и подключиться, отключаем поля ввода и наконец открываем соединение (myConnection.start().done(...).

Для отправления на сервер используется метод myConnection.send, в который в качестве параметра передаются введенное сообщение и сохраненный в скрытом поле ник. С помощью функции JSON.stringify данные превращаются в объект json, который потом сервер извлекает в методе protected override Task OnReceived(IRequest request, string connectionId, string data) из последнего параметра.

По сути небольшой примитивный чат готов, мы можем применить к нему стили по своему вкусу и протестировать:

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850