본문 바로가기
ASP.NET Core

.NET 6] SignalR Server, SignalR Client App

by Fastlane 2022. 6. 9.
728x90
반응형

출처 : https://dotnetplaybook.com/which-is-best-websockets-or-signalr/

Http, XMLHttpRequest & WebSockets 개념

Http

Request / Response Cycle

동기 Synchronous

Stateless : 요청하고 응답받고의 반복이며, 어떠한 연결을 유지하거나 상태값을 관리하지 않는다. 

 

XMLHttpRequest (Ajax)

부분 UI 업데이트가 가능하다. 

비동기 Asynchronous

Long Polling에서 사용된다. 

 

WebSockets 

Http "upgrade" 요청으로 시작한다. 

양방향 통신이 가능하다. Bi-directional

Server와 Client의 연결이 유지된다. Persistent

 

WebSocket API

  • Method
    • Send
    • Close
  • Events
    • Open
    • Message
    • Error
    • Close

 

SignalR

WebSocket을 감싸는 프레임워크이다. 

1. SignalR Server 

  • vscode terminal에 아래 명령어를 실행하여, web project를 생성한다. 
dotnet new web -n SignalRServer
PS C:\Users\admin\source\repos> dotnet new web -n SignalRServer
"ASP.NET Core Empty" 템플릿이 성공적으로 생성되었습니다.

생성 후 작업 처리 중...
C:\Users\admin\source\repos\SignalRServer\SignalRServer.csproj에서 'dotnet restore' 실행 중 ...
  복원할 프로젝트를 확인하는 중...
  C:\Users\admin\source\repos\SignalRServer\SignalRServer.csproj을(를) 80 ms 동안 복원했습니다.
복원에 성공했습니다.
  • 프로젝트 폴더에 Hubs 폴더를 만듭니다.
  • Hubs 폴더에서 다음 코드로 ChatHub 클래스를 만듭니다. (연결, 그룹 및 메시징 관리)

 

using System;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace SignalRServer
{
    public class ChatHub : Hub
    {
        public override Task OnConnectedAsync()
        {
            Console.WriteLine("--> Connection Established" + Context.ConnectionId); //Guid로 id를 생성할 필요가 없다. 
            Clients.Client(Context.ConnectionId).SendAsync("ReceiveConnID", Context.ConnectionId);
            return base.OnConnectedAsync();
        }

        public async Task SendMessageAsync(string message)
        {
            var routeOb = JsonConvert.DeserializeObject<dynamic>(message);
            string toClient = routeOb.To;
            Console.WriteLine("Message Received on: " + Context.ConnectionId);

            if(toClient == string.Empty)
            {
                await Clients.All.SendAsync("ReceiveMessage", message);
            }
            else
            {
                await Clients.Client(toClient).SendAsync("ReceiveMessage", message);
            }
        }


    }
}
  • Program.cs 파일에 SignalR 서버 구성
using SignalRServer;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors();
builder.Services.AddSignalR();

var app = builder.Build();

app.UseCors(builder => builder
    .WithOrigins("null")
    .AllowAnyHeader()
    .AllowAnyMethod()
    .AllowCredentials()
);


app.MapGet("/", () => "Hello World!");
app.MapHub<ChatHub>("/chatHub");

app.Run();
  • 서버실행 dotnet run!!!

2. SignalR Client

  • SignalRClient 폴더 생성
  • Signalr.js 파일 추가 
PS C:\Users\admin\source\repos\SignalRClient> libman --version
libman : 'libman' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 인식되지 않습니다. 이름이 정확한지 확인하고 경로가 포함된 경우   
경로가 올바른지 검증한 다음 다시 시도하십시오.
위치 줄:1 문자:1
+ libman --version
+ ~~~~~~
    + CategoryInfo          : ObjectNotFound: (libman:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
 
PS C:\Users\admin\source\repos\SignalRClient> dotnet tool install -g Microsoft.Web.LibraryManager.cli
다음 명령을 사용하여 도구를 호출할 수 있습니다. libman
'microsoft.web.librarymanager.cli' 도구('2.1.161' 버전)가 설치되었습니다.
PS C:\Users\admin\source\repos\SignalRClient> libman --version
2.1.161+abc97ecc7d.RR
PS C:\Users\admin\source\repos\SignalRClient> libman install @aspnet/signalr@next -p unpkg -d lib/signalr --files dist/browser/signalr.js
https://unpkg.com/@aspnet/signalr@next/dist/browser/signalr.js 파일을 다운로드하는 중...
lib/signalr/dist/browser/signalr.js이(가) 디스크에 기록되었습니다.
"@aspnet/signalr@next" 라이브러리를 "lib/signalr"에 설치했습니다.
  • signalRClient.html 파일 추가 
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>SignalR JavaScript Client</title>
</head>
<script src="lib/signalr/dist/browser/signalr.js"></script>

<body>
    <h1>SignalR JavaScript Client</h1>
    <p id="stateLabel">Ready to connect</p>
    <p id="connIDLabel">ConnID: N/a</p>
    <div>
        <label for="connectionUrl">SignalR Server URL:</label>
        <input id="connectionUrl" />
        <button id="connectButton" type="submit">Connect</button>
        <button id="closeButton" disabled>Close Socket</button>
    </div>
    <p></p>
    <div>
        <label for="sendMessage">Message:</label>
        <input id="sendMessage" disabled />
        <button id="sendButton" type="submit" disabled>Send</button>
    </div>
    <p></p>
    <div>
        <label for="recipients">Recipient IDs:</label>
        <input id="recipients" disabled />
    </div>
    <p></p>
    <h2>Communication Log</h2>
    <table style="width: 800px">
        <thead>
            <tr>
                <td style="width: 100px">From</td>
                <td style="width: 100px">To</td>
                <td>Data</td>
            </tr>
        </thead>
        <tbody id="commsLog">
        </tbody>
    </table>
    <p></p>
</body>
<script>
    var connectionUrl = document.getElementById("connectionUrl");
    var connectButton = document.getElementById("connectButton");
    var stateLabel = document.getElementById("stateLabel");
    var sendMessage = document.getElementById("sendMessage");
    var sendButton = document.getElementById("sendButton");
    var commsLog = document.getElementById("commsLog");
    var closeButton = document.getElementById("closeButton");
    var recipients = document.getElementById("recipients");
    var connID = document.getElementById("connIDLabel");
    connectionUrl.value = "http://localhost:5000/chatHub";

    var hubConnection = new signalR.HubConnectionBuilder().withUrl(connectionUrl.value).build();

    connectButton.onclick = function () {
        stateLabel.innerHTML = "Attempting to connect...";

        hubConnection.start().then(function () {
            updateState();
            commsLog.innerHTML += '<tr>' +
                '<td colspan="3" class="commslog-data">Connection opened</td>' +
                '</tr>';
        });

    };

    closeButton.onclick = function () {

        if (!hubConnection || hubConnection.state !== "Connected") {
            alert("Hub not connected!");
        }

        hubConnection.stop().then(function () {
            console.debug("Requested stop on hub");
        });

        hubConnection.onclose(function (event) {
            updateState();
            commsLog.innerHTML += '<tr>' +
                '<td colspan="3" class="commslog-data">Connection Stopped.</td>' +
                '</tr>';
        });

    };

    sendButton.onclick = function () {

        var message = constructJSON();
        hubConnection.invoke("SendMessageAsync", message);
        commsLog.innerHTML += '<tr>' +
            '<td class="commslog-server">Client</td>' +
            '<td class="commslog-client">Server</td>' +
            '<td class="commslog-data">' + htmlEscape(message) + '</td></tr>';
    };

    hubConnection.on("ReceiveConnID", function (connid) {
        connID.innerHTML = connid;
        commsLog.innerHTML += '<tr>' +
            '<td colspan="3" class="commslog-data">Connection ID Received from Hub</td>' +
            '</tr>';
    });

    hubConnection.on("ReceiveMessage", function (message) {
        commsLog.innerHTML += '<tr>' +
            '<td class="commslog-server">Server</td>' +
            '<td class="commslog-client">Client</td>' +
            '<td class="commslog-data">' + htmlEscape(message) + '</td></tr>';
    });

    function constructJSON() {
        return JSON.stringify({
            "From": connID.innerHTML.substring(8, connID.innerHTML.length),
            "To": recipients.value,
            "Message": sendMessage.value
        });
    }

    function htmlEscape(str) {
        return str.toString()
            .replace(/&/g, '&amp;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
    }

    function updateState() {
        function disable() {
            sendMessage.disabled = true;
            sendButton.disabled = true;
            closeButton.disabled = true;
            recipients.disabled = true;
        }
        function enable() {
            sendMessage.disabled = false;
            sendButton.disabled = false;
            closeButton.disabled = false;
            recipients.disabled = false;
        }
        connectionUrl.disabled = true;
        connectButton.disabled = true;
        if (!hubConnection) {
            disable();
        } else {
            switch (hubConnection.state) {
                case "Disconnected":
                    stateLabel.innerHTML = "Closed";
                    connID.innerHTML = "ConnID: N/a";
                    disable();
                    connectionUrl.disabled = false;
                    connectButton.disabled = false;
                    break;
                case "Connecting":
                    stateLabel.innerHTML = "Connecting...";
                    disable();
                    break;
                case "Connected":
                    stateLabel.innerHTML = "Connected";
                    enable();
                    break;
                default:
                    stateLabel.innerHTML = "Unknown WebSocket State - unknown";
                    disable();
                    break;
            }
        }
    }
</script>

</html>
  • signalRClient.html 파일 브라우저에서 열기

Connect 클릭!

메시지 Send 클릭!!

 

728x90
반응형

댓글