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

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

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

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

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

Microsoft ASP.NET SignalR 2

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

Сразу после установки библиотеки, чтобы задействовать фукциональность SignalR, добавим в проект следующий класс с названием Startup:

using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SignalRMvc.Startup))]
namespace SignalRMvc
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
} 

Для этого можно добавить обычный класс, а можно и специальный тип файлов OWIN Startup Class:

OWIN Startup Class

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

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

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

SignalR Hub Class

Новый класс будет иметь следующий код:

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

namespace SignalRMvc.Hubs
{
    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.Any(x => x.ConnectionId == id))
            {
                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(bool stopCalled)
        {
            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(stopCalled);
        }
    }
}

Как уже ранее говорилось, 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 для текущего клиента.

Теперь создадим представление для чата. По умолчанию в проекте уже есть контроллер HomeController с несколькими методами, для которых определены представления. Мы можем использовать, например, представление Index.cshtml. Изменим его содержимое следующим образом:

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <h2>Чат-комната</h2>

    <div class="main">
        <div id="loginBlock">
            Введите логин:<br />
            <input id="txtUserName" type="text" />
            <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"></div>

            <div id="chatusers">
                <p><b>Все пользователи</b></p>
            </div>
        </div>
        <input id="hdId" type="hidden" />
        <input id="username" type="hidden" />
    </div>

    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <!--Ссылка на библиотеку SignalR -->
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
    <!--Ссылка на автоматически сгенерированный скрипт хаба SignalR -->
    <script src="~/signalr/hubs"></script>
    <script src="~/Scripts/util.js"></script>
</body>
</html>

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

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

Основные действия на клиентской части будет выполнять код javascript. Причем перед подключением всех остальных файлов скриптов идет подключение библиотеки jQuery, поскольку скрипт SignalR имеет зависимости от jQuery.

Далее подключается сама библиотека SignalR. Конкретная версия библиотеки может отличаться от использованной выше. Затем подключается скрипт signalr/hubs. Хотя на самом деле пока его нет, но в процессе запуска приложения он будет автоматически генерироваться и после этого использоваться.

Последним идет подключение скрипта util.js, которого пока еще нет. Итак, добавим файл с данным названием в папку Scripts и определим в нем следующий код:

$(function () {

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

    // Функция, вызываемая при подключении нового пользователя
    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").append('<p id="' + id + '"><b>' + name + '</b></p>');
    }
}

Весь код на стороне клиента находится в функции 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 в их названии используется малая буква.

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

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

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

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

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