Первое приложение с SignalR

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

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

Создадим простенькое приложение с использованием SignalR - небольшой чат. Итак, создадим новое приложение ASP.NET MVC4 по шаблону Basic. Я назвал свое приложение SignalRMvc.

После создания проекта найдем через пакетный менеджер NuGet библиотеку Microsoft ASP.NET SignalR:

Microsoft ASP.NET SignalR

Установим ее. После этого в папку библиотек Referenses будет добавлен ряд библиотек SignalR, а в каталог скриптов Scripts будет добавлен клиентский скрипт jquery.signalR-[номер_версии].js и его минимизированный аналог.

Поскольку приложение чата оперирует пользователями, то создадим модель пользователей. Добавим в папку Models класс User:

public class User
{
    public string ConnectionId { get; set; }
    public string Name { get; set; }
}

Затем в папку Controllers добавим пустой контроллер HomeController, где будет один метод Index:

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

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

Больше нам ничего не надо, всю логику приложения будет содержать хаб.

Теперь добавим в проект новую папку. Назовем ее Hubs. В ней будут находиться хабы нашего приложения. В эту папку добавим новый класс ChatHub.cs. Он будет иметь следующий код:

using System;
using System.Web;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.SignalR;
using SignalRMvc.Models;

namespace SignalRChat
{
    public class ChatHub : Hub
    {
        static List<User> Users = new List<User>();
        
        // Отправка сообщений
        public void Send(string name, string message)
        {
            Clients.All.addMessage(name, message);
        }

        // Подключение нового пользователя
        public void Connect(string userName)
        {
            var id = Context.ConnectionId;


            if (Users.Count(x => x.ConnectionId == id) == 0)
            {
                Users.Add(new User { ConnectionId = id, Name = userName });

                // Посылаем сообщение текущему пользователю
                Clients.Caller.onConnected(id, userName, Users);

                // Посылаем сообщение всем пользователям, кроме текущего
                Clients.AllExcept(id).onNewUserConnected(id, userName);
            }
        }

        // Отключение пользователя
        public override System.Threading.Tasks.Task OnDisconnected()
        {
            var item = Users.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
            if (item != null)
            {
                Users.Remove(item);
                var id = Context.ConnectionId;
                Clients.All.onUserDisconnected(id, item.Name);
            }

            return base.OnDisconnected();
        }
    }
}

Как уже ранее говорилось, SignalR использует две модели взаимодействия сервера и клиента: Persistent Connection и хабы. В данном случае мы используем хабы. Для этого создаем свой хаб ChatHub, который наследуется от класса Hub.

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

Далее у нас определен ряд методов для отправки сообщений, подключения и отключения пользователей. Разберем метод Send, который предназначен В нем вызывается единственный метод: Clients.All.addMessage(name, message);. Объект Clients означает коллекцию всех пользователей хаба. Свойство All, идущее далее, говорит о том, что метод надо применить у всех подключенных клиентов.

Формат вызова методов клиента

  • Вызов метода на всех клиентах: Clients.All.addMessage(name, message);

  • Вызов метода только на текущем клиенте, который обратился к серверу: Clients.Caller.addMessage(name, message);

  • Вызов метода на всех клиентах, кроме того, который обратился к серверу: Clients.Others.addMessage(name, message);

  • Вызов метода только у клиента с определенным id: Clients.Client(Context.ConnectionId).addMessage(name, message);

  • Вызов метода на всех клиентах, кроме клиента с определенным id: Clients.AllExcept(connectionId).addMessage(name, message);

  • Вызов метода на всех клиентах указанной группы: Clients.Group(groupName).addMessage(name, message);

  • Вызов метода на всех клиентах указанной группы, за исключением клиента, у которого id - connectionId: Clients.Group(groupName, connectionId).addMessage(name, message);

  • Вызов метода на всех клиентах указанной группы, за исключением обратившегося к серверу клиента: Clients.OthersInGroup(groupName).addMessage(name, message);

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

Далее в выражении следует метод addMessage. Этот метод объявляется на стороне клиента в коде javascript. Чуть позже мы добавим код клиентской стороны. А пока просто надо знать, что эти методы находятся не на серверной части, а на стороне клиента.

В методе Connect мы сначала получаем id текущего пользователя, который и обратился к методу Connect, через объект Context.ConnectionId. Этот id задается средой и хранит строковое значение (не числовое). Затем вызываем методы на клиенте через объект Clients.

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

Затем для метода Index ранее созданного контроллера Home создадим представление Index.cshtml:

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

<h2>Чат-комната</h2>

<div class="main">
    <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 id="chatusers">
            <p><strong>Все пользователи</strong></p>
            <ul></ul>
        </div>
    </div>
    <input id="hdId" type="hidden" />
    <input id="username" type="hidden" />
</div>
@section scripts {
    <!--Ссылка на библиотеку SignalR -->
    <script src="~/Scripts/jquery.signalR-1.1.2.min.js"></script>
    <!--Ссылка на автоматически сгенерированный скрипт хаба SignalR -->
    <script src="~/signalr/hubs"></script> 
    <script>
        $(function () {

            $('#loginBlock').show();
            $('#chatBody').hide();
            // Ссылка на автоматически-сгенерированный прокси хаба
            var chat = $.connection.chatHub;
            // Объявление функции, которая хаб вызывает при получении сообщений
            chat.client.addMessage = function (name, message) {
                // Добавление сообщений на веб-страницу 
                $('#chatroom ul').append('<li><strong>' + htmlEncode(name)
                    + '</strong>: ' + htmlEncode(message) + '</li>');
            };

            // Функция, вызываемая при подключении нового пользователя
            chat.client.onConnected = function (id, userName, allUsers) {

                $('#loginBlock').hide();
                $('#chatBody').show();
                // установка в скрытых полях имени и id текущего пользователя
                $('#hdId').val(id);
                $('#username').val(userName);
                $('#header').html('<h3>Добро пожаловать, ' + userName + '</h3>');

                // Добавление всех пользователей
                for (i = 0; i < allUsers.length; i++) {

                    AddUser(allUsers[i].ConnectionId, allUsers[i].Name);
                }
            }

            // Добавляем нового пользователя
            chat.client.onNewUserConnected = function (id, name) {

                AddUser(id, name);
            }

            // Удаляем пользователя
            chat.client.onUserDisconnected = function (id, userName) {

                $('#' + id).remove();
            }

            // Открываем соединение
            $.connection.hub.start().done(function () {

                $('#sendmessage').click(function () {
                    // Вызываем у хаба метод Send
                    chat.server.send($('#username').val(), $('#message').val());
                    $('#message').val('');
                });

                // обработка логина
                $("#btnLogin").click(function () {

                    var name = $("#txtUserName").val();
                    if (name.length > 0) {
                        chat.server.connect(name);
                    }
                    else {
                        alert("Введите имя");
                    }
                });
            });
        });
        // Кодирование тегов
        function htmlEncode(value) {
            var encodedValue = $('<div />').text(value).html();
            return encodedValue;
        }
        //Добавление нового пользователя
        function AddUser(id, name) {

            var userId = $('#hdId').val();
            if (userId != id){

                $("#chatusers ul").append('<li id="' + id + '" class="user" >' + name + '</li>');
            }
        }
    </script>
}

Разметка содержит по сути два блока: loginBlock (блок ввода логина) и chatBody (сам чат). В один момент времени виде только один блок, поэтому, если пользователь удачно подключился, то мы переключаем видимость, делая видимым блок chatBody.

Блок chatBody содержит три подблока: inputForm (форма ввода сообщения), chatroom (поле, куда будут добавляться сообщения) и chatusers (список всех пользователей, кроме текущего).

Теперь перейдем к коду клиентской части. Обратите внимание, что весь javascript у нас находится в специальной секции скриптов, которая подключается в мастер-страницу после библиотеки jQuery. Если у вас несколько другая структура приложения, то перед данным скриптом следует подключать основную библиотеку jQuery.

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

Весь код находится в функции jQuery, за ее пределами определены две функции - htmlEncode (для кодирования тегов, чтобы пресечь возможные попытки вставок кода javascript и прочие нехорошести) и AddUser (для добавления данных пользователя в список).

Чтобы взаимодействовать с хабом, получаем прокси хаба: var chat = $.connection.chatHub;. Затем определяем ряд функций клиента chat.client.addMessage, chat.client.onConnected и т.д.

Выше в хабе в коде C# у нас было определено обращение к функциям клиента: Clients.All.addMessage(name, message);. Функция addMessage - это и есть функция, определенная для chat.client.addMessage. Подобным образом мы можем обращаться на сервере и к другим функциям клиента.

Для открытия подключения мы вызываем метод $.connection.hub.start().done(), передавая в него функцию. В этой функции мы вешаем обработчики кнопок, по нажатию на которые происходит обращение на сервер.

Например, отправку сообщения производится с помощью вызова chat.server.send($('#username').val(), $('#message').val());. Выражение chat.server представляет собой обращение к методам хаба на сервере. То есть в данном случае будет идти обращение к методу public void Send(string name, string message), определенному в классе ChatHub.

Подобным образом срабатывает вызов метода chat.server.connect(name);. Обратите внимание, что хотя в коде c# методы объявлены с большой буквы, в коде javascript в их названии используется малая буква.

И в заключении нам надо зарегистрировать хаб. Для этого откроем файл Global.asax и добавим в метод Application_Start строку RouteTable.Routes.MapHubs(), которая и производит регистрацию:

protected void Application_Start()
{
    RouteTable.Routes.MapHubs();
	AreaRegistration.RegisterAllAreas();

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

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

Теперь мы можем проверить в действии. После запуска нам будет предложено ввести логин:

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

Таким образом, слева у нас список с сообщениями, а справа - список всех пользователей, кроме текущего.

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