자습서: SignalR 2를 사용하여 고주파 실시간 앱 만들기
이 자습서에서는 ASP.NET SignalR 2를 사용하여 고주파 메시징 기능을 제공하는 웹 애플리케이션을 만드는 방법을 보여 줍니다. 이 경우 "고주파 메시징"은 서버가 고정된 속도로 업데이트를 전송한다는 것을 의미합니다. 초당 최대 10명의 메시지를 보냅니다.
사용자가 만든 애플리케이션에는 사용자가 끌 수 있는 셰이프가 표시됩니다. 서버는 모든 연결된 브라우저에서 셰이프의 위치를 시간 제한 업데이트를 사용하여 끌어간 셰이프의 위치와 일치하도록 업데이트합니다.
이 자습서에서 도입된 개념에는 실시간 게임 및 기타 시뮬레이션 애플리케이션의 애플리케이션이 있습니다.
이 자습서에서는 다음을 수행합니다.
- 프로젝트 설정
- 기본 애플리케이션 만들기
- 앱이 시작될 때 허브에 매핑
- 클라이언트 추가
- 앱 실행
- 클라이언트 루프 추가
- 서버 루프 추가
- 부드러운 애니메이션 추가
Warning
이 설명서는 최신 버전의 SignalR용이 아닙니다. ASP.NET Core SignalR을 살펴보세요.
필수 조건
프로젝트 설정
이 섹션에서는 Visual Studio 2017에서 프로젝트를 만듭니다.
이 섹션에서는 Visual Studio 2017을 사용하여 빈 ASP.NET 웹 애플리케이션을 만들고 SignalR 및 jQuery.UI 라이브러리를 추가하는 방법을 보여줍니다.
Visual Studio에서 ASP.NET 웹 애플리케이션을 만듭니다.
새 ASP.NET 웹 애플리케이션 - MoveShapeDemo 창에서 비어 있는 상태로 선택한 상태로 두고 확인을 선택합니다.
솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가>새 항목을 선택합니다.
새 항목 추가 - MoveShapeDemo에서 설치된>Visual C#>Web>SignalR을 선택한 다음 SignalR Hub 클래스(v2)를 선택합니다.
MoveShapeHub 클래스의 이름을 지정하고 프로젝트에 추가합니다.
이 단계에서는 MoveShapeHub.cs 클래스 파일을 만듭니다. 동시에 SignalR을 지원하는 스크립트 파일 및 어셈블리 참조 집합을 프로젝트에 추가합니다.
도구>NuGet 패키지 관리자>패키지 관리자 콘솔을 선택합니다.
패키지 관리자 콘솔에서 다음 명령을 실행합니다.
Install-Package jQuery.UI.Combined
이 명령은 jQuery UI 라이브러리를 설치합니다. 셰이프에 애니메이션 효과를 적용하는 데 사용합니다.
솔루션 탐색기 스크립트 노드를 확장합니다.
jQuery, jQueryUI 및 SignalR에 대한 스크립트 라이브러리가 프로젝트에 표시됩니다.
기본 애플리케이션 만들기
이 섹션에서는 브라우저 애플리케이션을 만듭니다. 앱은 각 마우스 이동 이벤트 중에 셰이프의 위치를 서버로 보냅니다. 서버는 이 정보를 다른 모든 연결된 클라이언트에 실시간으로 브로드캐스트합니다. 이후 섹션에서 이 애플리케이션에 대해 자세히 알아봅니다.
MoveShapeHub.cs 파일을 엽니다.
MoveShapeHub.cs 파일의 코드를 다음 코드로 바꿉다.
using Microsoft.AspNet.SignalR; using Newtonsoft.Json; namespace MoveShapeDemo { public class MoveShapeHub : Hub { public void UpdateModel(ShapeModel clientModel) { clientModel.LastUpdatedBy = Context.ConnectionId; // Update the shape model within our broadcaster Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel); } } public class ShapeModel { // We declare Left and Top as lowercase with // JsonProperty to sync the client and server models [JsonProperty("left")] public double Left { get; set; } [JsonProperty("top")] public double Top { get; set; } // We don't want the client to get the "LastUpdatedBy" property [JsonIgnore] public string LastUpdatedBy { get; set; } } }
파일을 저장합니다.
MoveShapeHub
클래스는 SignalR 허브의 구현입니다. SignalR 시작 자습서와 마찬가지로 허브에는 클라이언트가 직접 호출하는 메서드가 있습니다. 이 경우 클라이언트는 셰이프의 새 X 및 Y 좌표가 있는 개체를 서버로 보냅니다. 이러한 좌표는 다른 모든 연결된 클라이언트에 브로드캐스트됩니다. SignalR은 JSON을 사용하여 이 개체를 자동으로 직렬화합니다.
앱은 클라이언트에 ShapeModel
개체를 보냅니다. 셰이프의 위치를 저장할 멤버가 있습니다. 서버의 개체 버전에는 어떤 클라이언트의 데이터가 저장되고 있는지 추적하는 멤버도 있습니다. 이 개체는 서버가 클라이언트의 데이터를 다시 자체로 보내지 못하도록 합니다. 이 멤버는 JsonIgnore
특성을 사용하여 애플리케이션이 데이터를 직렬화하고 클라이언트로 다시 보내지 않도록 합니다.
앱이 시작될 때 허브에 매핑
다음으로, 애플리케이션이 시작될 때 허브에 대한 매핑을 설정합니다. SignalR 2에서 OWIN 시작 클래스를 추가하면 매핑이 만들어집니다.
솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가>새 항목을 선택합니다.
새 항목 추가 - MoveShapeDemo에서 설치된>Visual C#>Web을 선택한 다음, OWIN 시작 클래스를 선택합니다.
시작 클래스의 이름을 지정하고 확인을 선택합니다.
Startup.cs 파일의 기본 코드를 다음 코드로 바꿉다.
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))] namespace MoveShapeDemo { public class Startup { public void Configuration(IAppBuilder app) { // Any connection or hub wire up and configuration should go here app.MapSignalR(); } } }
OWIN 시작 클래스는 앱이 메서드를 실행할 때 호출 MapSignalR
합니다 Configuration
. 앱은 어셈블리 특성을 사용하여 OWIN의 시작 프로세스에 클래스를 OwinStartup
추가합니다.
클라이언트 추가
클라이언트에 대한 HTML 페이지를 추가합니다.
솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 HTML 페이지 추가>를 선택합니다.
페이지 이름을 기본값으로 지정하고 확인을 선택합니다.
솔루션 탐색기 Default.html 마우스 오른쪽 단추로 클릭하고 시작 페이지로 설정을 선택합니다.
Default.html 파일의 기본 코드를 다음 코드로 바꿉다.
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.1.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), shapeModel = { left: 0, top: 0 }; moveShapeHub.client.updateShape = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moveShapeHub.server.updateModel(shapeModel); } }); }); }); </script> <div id="shape" /> </body> </html>
솔루션 탐색기 스크립트를 확장합니다.
jQuery 및 SignalR에 대한 스크립트 라이브러리는 프로젝트에 표시됩니다.
Important
패키지 관리자는 이후 버전의 SignalR 스크립트를 설치합니다.
프로젝트의 스크립트 파일 버전에 해당하도록 코드 블록의 스크립트 참조를 업데이트합니다.
이 HTML 및 JavaScript 코드는 빨간색이라는 shape
빨강 div
을 만듭니다. jQuery 라이브러리를 사용하여 셰이프의 끌기 동작을 사용하도록 설정하고 이벤트를 사용하여 drag
셰이프의 위치를 서버로 보냅니다.
앱 실행
앱을 실행하여 작동할 수 있습니다. 셰이프를 브라우저 창 주위로 끌면 다른 브라우저에서도 셰이프가 이동합니다.
도구 모음에서 스크립트 디버깅을 켜고 재생 단추를 선택하여 디버그 모드에서 애플리케이션을 실행합니다.
오른쪽 위 모서리에 빨간색 셰이프가 있는 브라우저 창이 열립니다.
페이지의 URL을 복사합니다.
다른 브라우저를 열고 URL을 주소 표시줄에 붙여넣습니다.
브라우저 창 중 하나에서 셰이프를 끌어옵니다. 다른 브라우저 창의 셰이프는 다음과 같습니다.
애플리케이션이 이 메서드를 사용하는 동안 권장되는 프로그래밍 모델은 아닙니다. 전송되는 메시지 수에는 상한이 없습니다. 따라서 클라이언트와 서버는 메시지와 성능 저하로 인해 과부하가 발생합니다. 또한 앱은 클라이언트에 분리된 애니메이션을 표시합니다. 이 육포 애니메이션은 셰이프가 각 메서드에 의해 즉시 이동하기 때문에 발생합니다. 셰이프가 각 새 위치로 원활하게 이동하는 경우 더 좋습니다. 다음으로, 이러한 문제를 해결하는 방법을 알아봅니다.
클라이언트 루프 추가
모든 마우스 이동 이벤트에서 셰이프의 위치를 보내면 불필요한 양의 네트워크 트래픽이 생성됩니다. 앱은 클라이언트에서 메시지를 제한해야 합니다.
javascript setInterval
함수를 사용하여 고정 속도로 서버에 새 위치 정보를 보내는 루프를 설정합니다. 이 루프는 "게임 루프"의 기본 표현입니다. 게임의 모든 기능을 구동하는 반복적으로 호출되는 함수입니다.
Default.html 파일의 클라이언트 코드를 다음 코드로 바꿉다.
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.1.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), // Send a maximum of 10 messages per second // (mouse movements trigger a lot of messages) messageFrequency = 10, // Determine how often to send messages in // time to abide by the messageFrequency updateRate = 1000 / messageFrequency, shapeModel = { left: 0, top: 0 }, moved = false; moveShapeHub.client.updateShape = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moved = true; } }); // Start the client side server update interval setInterval(updateServerModel, updateRate); }); function updateServerModel() { // Only update server if we have a new movement if (moved) { moveShapeHub.server.updateModel(shapeModel); moved = false; } } }); </script> <div id="shape" /> </body> </html>
Important
스크립트 참조를 다시 바꿔야 합니다. 프로젝트의 스크립트 버전과 일치해야 합니다.
이 새 코드는 함수를 추가합니다
updateServerModel
. 고정된 빈도로 호출됩니다. 이 함수는 플래그가 보낼 새 위치 데이터가 있음을 표시할 때마다moved
위치 데이터를 서버로 보냅니다.재생 단추를 선택하여 애플리케이션 시작
페이지의 URL을 복사합니다.
다른 브라우저를 열고 URL을 주소 표시줄에 붙여넣습니다.
브라우저 창 중 하나에서 셰이프를 끌어옵니다. 다른 브라우저 창의 셰이프는 다음과 같습니다.
앱은 서버로 전송되는 메시지 수를 제한하므로 애니메이션은 처음에는 부드러운 것처럼 표시되지 않습니다.
서버 루프 추가
현재 애플리케이션에서 서버에서 클라이언트로 전송된 메시지는 수신되는 빈도만큼 자주 전송됩니다. 이 네트워크 트래픽은 클라이언트에서 볼 수 있는 것과 비슷한 문제를 표시합니다.
앱은 필요한 것보다 더 자주 메시지를 보낼 수 있습니다. 결과적으로 연결이 플러드될 수 있습니다. 이 섹션에서는 보내는 메시지의 속도를 제한하는 타이머를 추가하도록 서버를 업데이트하는 방법을 설명합니다.
MoveShapeHub.cs
의 내용을 다음 코드로 바꿉니다.using System; using System.Threading; using Microsoft.AspNet.SignalR; using Newtonsoft.Json; namespace MoveShapeDemo { public class Broadcaster { private readonly static Lazy<Broadcaster> _instance = new Lazy<Broadcaster>(() => new Broadcaster()); // We're going to broadcast to all clients a maximum of 25 times per second private readonly TimeSpan BroadcastInterval = TimeSpan.FromMilliseconds(40); private readonly IHubContext _hubContext; private Timer _broadcastLoop; private ShapeModel _model; private bool _modelUpdated; public Broadcaster() { // Save our hub context so we can easily use it // to send to its connected clients _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>(); _model = new ShapeModel(); _modelUpdated = false; // Start the broadcast loop _broadcastLoop = new Timer( BroadcastShape, null, BroadcastInterval, BroadcastInterval); } public void BroadcastShape(object state) { // No need to send anything if our model hasn't changed if (_modelUpdated) { // This is how we can access the Clients property // in a static hub method or outside of the hub entirely _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model); _modelUpdated = false; } } public void UpdateShape(ShapeModel clientModel) { _model = clientModel; _modelUpdated = true; } public static Broadcaster Instance { get { return _instance.Value; } } } public class MoveShapeHub : Hub { // Is set via the constructor on each creation private Broadcaster _broadcaster; public MoveShapeHub() : this(Broadcaster.Instance) { } public MoveShapeHub(Broadcaster broadcaster) { _broadcaster = broadcaster; } public void UpdateModel(ShapeModel clientModel) { clientModel.LastUpdatedBy = Context.ConnectionId; // Update the shape model within our broadcaster _broadcaster.UpdateShape(clientModel); } } public class ShapeModel { // We declare Left and Top as lowercase with // JsonProperty to sync the client and server models [JsonProperty("left")] public double Left { get; set; } [JsonProperty("top")] public double Top { get; set; } // We don't want the client to get the "LastUpdatedBy" property [JsonIgnore] public string LastUpdatedBy { get; set; } } }
재생 단추를 선택하여 애플리케이션을 시작합니다.
페이지의 URL을 복사합니다.
다른 브라우저를 열고 URL을 주소 표시줄에 붙여넣습니다.
브라우저 창 중 하나에서 셰이프를 끌어옵니다.
이 코드는 클라이언트를 확장하여 클래스를 추가합니다 Broadcaster
. 새 클래스는 .NET Framework의 클래스를 Timer
사용하여 나가는 메시지를 제한합니다.
허브 자체가 일시적이라는 것을 배우는 것이 좋습니다. 필요할 때마다 만들어집니다. 따라서 앱은 싱글톤으로 만듭니다 Broadcaster
. 지연 초기화를 사용하여 필요할 때까지 '의 생성을 연기 Broadcaster
합니다. 그러면 앱이 타이머를 시작하기 전에 첫 번째 허브 인스턴스를 완전히 만듭니다.
그런 다음 클라이언트의 UpdateShape
함수에 대한 호출이 허브의 UpdateModel
메서드에서 이동합니다. 앱이 들어오는 메시지를 받을 때마다 더 이상 즉시 호출되지 않습니다. 대신 앱은 초당 25개의 호출 속도로 클라이언트에 메시지를 보냅니다. 프로세스는 클래스 내에서 타이머에 의해 _broadcastLoop
관리됩니다 Broadcaster
.
마지막으로, 허브에서 클라이언트 메서드를 직접 호출하는 대신 클래스는 Broadcaster
현재 운영 중인 _hubContext
허브에 대한 참조를 가져와야 합니다. 를 사용하여 참조를 GlobalHost
가져옵니다.
부드러운 애니메이션 추가
애플리케이션이 거의 완료되었지만 한 번 더 개선할 수 있습니다. 앱은 서버 메시지에 대한 응답으로 클라이언트의 셰이프를 이동합니다. 셰이프의 위치를 서버에서 제공하는 새 위치로 설정하는 대신 JQuery UI 라이브러리의 animate
함수를 사용합니다. 셰이프를 현재 위치와 새 위치 간에 원활하게 이동할 수 있습니다.
Default.html 파일에서 클라이언트의
updateShape
메서드를 강조 표시된 코드처럼 업데이트합니다.<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.1.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), // Send a maximum of 10 messages per second // (mouse movements trigger a lot of messages) messageFrequency = 10, // Determine how often to send messages in // time to abide by the messageFrequency updateRate = 1000 / messageFrequency, shapeModel = { left: 0, top: 0 }, moved = false; moveShapeHub.client.updateShape = function (model) { shapeModel = model; // Gradually move the shape towards the new location (interpolate) // The updateRate is used as the duration because by the time // we get to the next location we want to be at the "last" location // We also clear the animation queue so that we start a new // animation and don't lag behind. $shape.animate(shapeModel, { duration: updateRate, queue: false }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moved = true; } }); // Start the client side server update interval setInterval(updateServerModel, updateRate); }); function updateServerModel() { // Only update server if we have a new movement if (moved) { moveShapeHub.server.updateModel(shapeModel); moved = false; } } }); </script> <div id="shape" /> </body> </html>
재생 단추를 선택하여 애플리케이션을 시작합니다.
페이지의 URL을 복사합니다.
다른 브라우저를 열고 URL을 주소 표시줄에 붙여넣습니다.
브라우저 창 중 하나에서 셰이프를 끌어옵니다.
다른 창에서 셰이프의 이동은 덜 육포처럼 보입니다. 앱은 들어오는 메시지당 한 번 설정되지 않고 시간이 지남에 따라 이동을 보간합니다.
이 코드는 셰이프를 이전 위치에서 새 위치로 이동합니다. 서버는 애니메이션 간격 동안 셰이프의 위치를 제공합니다. 이 경우 100밀리초입니다. 앱은 새 애니메이션이 시작되기 전에 셰이프에서 실행 중인 이전 애니메이션을 지웁니다.
코드 가져오기
추가 리소스
SignalR에 대한 자세한 내용은 다음 리소스를 참조하세요.
다음 단계
이 자습서에서는 다음을 수행합니다.
- 프로젝트 설정
- 기본 애플리케이션 만들기
- 앱이 시작될 때 허브에 매핑됨
- 클라이언트 추가됨
- 앱 실행
- 클라이언트 루프 추가됨
- 서버 루프 추가됨
- 부드러운 애니메이션이 추가됨
다음 문서로 이동하여 ASP.NET SignalR 2를 사용하여 서버 브로드캐스트 기능을 제공하는 웹 애플리케이션을 만드는 방법을 알아봅니다.