Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Чат - наиболее показательное решение, которое можно сделать с помощью SignalR. Однако чатами все не ограничивается. И попробуем создать графическое приложение с использованием SignalR, в котром есть общее полотно, и каждый подключившийся к приложению может что-то нарисовать, и все будут это видеть.
Итак, создадим проект ASP.NET MVC 5 с типом аутентификации No Authentication. Я назвал свой проект SignalRDraw. И сразу же через менеджер NuGet добавим в проект библиотеку SignalR.
Сразу же создадим клиента для приложения. Для этого добавим в проект новый файл index.html прямо в корень проекта. Затем нажмем на этот файл правой кнопкой мыши и в контекстном меню выберем Set as Start Page (Сделать начальной страницей). Таким образом, мы не будем использовать ни контроллеры, ни их представления. Весь функционал клиента у нас будет содержать эта веб-страничка. А каталоги Controllers и Views в принципе в обще можно удалить.
Теперь изменим код файла index.html на следующий:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <style> canvas { position: relative; background-color:#ffd800; } </style> </head> <body> <canvas id='drawingpad' width='400' height='300'></canvas> <script src="/Scripts/jquery-1.10.2.min.js"></script> <!--Ссылка на библиотеку SignalR --> <script src="/Scripts/jquery.signalR-2.1.0.min.js"></script> <!--Ссылка на автоматически сгенерированный скрипт хаба SignalR --> <script src="/signalr/hubs"></script> <script> $(function () { var drawGame = { // указывает, происходит ли отрисовка isDrawing: false, // начальная точка следующей линии startX: 0, startY: 0, }; // модель линий var data = { startX:0, startY:0, endX:0, endY:0 }; // контекст элемента canvas var canvas = document.getElementById('drawingpad'); var ctx = canvas.getContext('2d'); // Ссылка на автоматически-сгенерированный прокси хаба var chat = $.connection.drawHub; // Объявление функции, которая хаб вызывает при получении сообщений chat.client.addLine = function (data) { // Добавление линий drawLine(ctx, data.startX, data.startY, data.endX, data.endY, 1); }; // Открываем соединение $.connection.hub.start().done(function () { // после открытия соединения устанавливаем обработчики мыши canvas.addEventListener("mousedown", mousedown, false); canvas.addEventListener("mousemove", mousemove, false); canvas.addEventListener("mouseup", mouseup, false); }); // просто рисуем линию function drawLine(ctx, x1, y1, x2, y2, thickness) { ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.lineWidth = thickness; ctx.strokeStyle = "#444"; ctx.stroke(); } // нажите мыши function mousedown(e) { // получаем позиции x и y относительно верхнего левого угла элемента canvas var mouseX = e.layerX || 0; var mouseY = e.layerY || 0; drawGame.startX = mouseX; drawGame.startY = mouseY; drawGame.isDrawing = true; }; // перемещение мыши function mousemove(e) { // рисуем линию, если isdrawing==true if (drawGame.isDrawing) { // получаем позиции x и y относительно верхнего левого угла элемента canvas var mouseX = e.layerX || 0; var mouseY = e.layerY || 0; if (!(mouseX == drawGame.startX && mouseY == drawGame.startY)) { drawLine(ctx, drawGame.startX, drawGame.startY, mouseX, mouseY, 1); data.startX = drawGame.startX; data.startY = drawGame.startY; data.endX = mouseX; data.endY = mouseY; chat.server.send(data); drawGame.startX = mouseX; drawGame.startY = mouseY; } } }; function mouseup(e) { drawGame.isDrawing = false; } }); </script> </body> </html>
Разметка html содержит всего один элемент canvas - полотно для рисования, больше нам ничего не надо.
В коде javascript вначале определяются две модели: drawGame
- для использования внутри клиента и data
- для взаимодействия с сервером.
Чтобы непоседственно рисовать примитивы на полотне, нам надо получить canvas и его контекст:
var canvas = document.getElementById('drawingpad'); var ctx = canvas.getContext('2d');
Далее получаем ссылку на хаб. В данном случае класс хаба на сервере, который мы позже создадим, будет называться DrawHub. А с помощью функции
addLine
клиент будет получать с сервера данные и по ним рисовать линии.
var chat = $.connection.drawHub; chat.client.addLine = function (data) { drawLine(ctx, data.startX, data.startY, data.endX, data.endY, 1); };
Затем в функции $.connection.hub.start().done(function ()...
добавляются обработчики событий мыши, с поомщью которых можно рисовать линии и
отправлять сообщения об этих линиях на сервер.
Теперь определим на уровне сервера модель, которая будет представлять данные, получаемые от клиента. Добавим в папку Models новый класс Data:
using System; using Newtonsoft.Json; namespace SignalRDraw.Models { public class Data { [JsonProperty("startX")] public float StartX { get; set; } [JsonProperty("startY")] public float StartY { get; set; } [JsonProperty("endX")] public float EndX { get; set; } [JsonProperty("endY")] public float EndY { get; set; } } }
Каждое свойство для связи с моделью data, определенной в javscript, имеет атрибут JsonProperty.
Теперь добавим в проект прямо в корень новый класс хаба:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using Microsoft.AspNet.SignalR; using SignalRDraw.Models; namespace SignalRDraw { public class DrawHub : Hub { public void Send(Data data) { Clients.AllExcept(Context.ConnectionId).addLine(data); } } }
С помощью метода Clients.AllExcept() мы можем исключить из рассылки сообщений того клиента, который собственно и прислал сообщений, и таким образом,
избежать ситуации, когда у него будет дублироваться нарисованная линия. А метод addLine
собственно и выполняет рыссылку всем
подключенным клиентам и задействует на клиенте одноименный метод addLine
, который выполняет рисование.
Ну и также добавим в проект класс Startup, чтобы задействовать SignalR:
using System; using System.Threading.Tasks; using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(SignalRDraw.Startup))] namespace SignalRDraw { public class Startup { public void Configuration(IAppBuilder app) { app.MapSignalR(); } } }
В итоге получится следующая структура
И теперь мы можем запустить проект и открыть запущенную страницу в разных браузерах и протестировать приложение: