서론
이전에는 ASP.NET의 긴 연결과 WebSocket에 대해 학습했습니다.
ASP.NET으로 긴 연결 구현 - chenxizhaolu - 블로그원 .NET에서 WebSocket 사용 사례 - chenxizhaolu - 블로그원
오늘은 SignalR을 학습하여 실시간 웹 기능三部曲을 마무리하겠습니다.
ASP.NET Core SignalR은 실시간 웹 기능을 애플리케이션에 추가하는 것을 단순화하는 오픈 소스 라이브러리입니다.실시간 웹 기능은 클라이언트가 주기적으로 서버를 폴링하여 새 데이터를 요청하는 대신, 서버 측 코드가 데이터를 사용 가능하게 되면 즉시 연결된 클라이언트에 콘텐츠를 푸시할 수 있게 합니다. SignalR은 채팅 애플리케이션, 실시간 대시보드, 게임 및 협업 도구 등과 같이 고빈도 데이터 업데이트가 필요한 시나리오에 특히 적합합니다.
간단한 채팅 대화상자
클라이언트가 메시지를 서버로 보내면 서버가 응답합니다.
서버 코드:
public class ChatHub : Hub
{
public async Task TransmitMessage(string username, string content)
{
await Clients.All.SendAsync("ReceiveMessage", username, "서버가 클라이언트 메시지를 수신했습니다: " + content);
}
public override async Task OnConnectedAsync()
{
await Clients.All.SendAsync("ReceiveMessage", "시스템", $"{Context.ConnectionId}이(가) 채팅에 참여했습니다");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await Clients.All.SendAsync("ReceiveMessage", "시스템", $"{Context.ConnectionId}이(가) 채팅을 나갔습니다");
await base.OnDisconnectedAsync(exception);
}
}
var appBuilder = WebApplication.CreateBuilder(args);
appBuilder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.WithOrigins("http://localhost:5065") // 모든 출처 허용
.AllowAnyMethod() // 모든 HTTP 메서드 허용
.AllowAnyHeader() // 모든 요청 허용
.AllowCredentials();
});
});
appBuilder.Services.AddSignalR();
appBuilder.WebHost.UseUrls("http://localhost:5000");
var app = appBuilder.Build();
// CORS 미들웨어 활성화
app.UseCors("AllowAll");
app.UseRouting();
app.MapHub<ChatHub>("/chatHub");
app.Run();
클라이언트 코드:
var webBuilder = WebApplication.CreateBuilder(args);
var app = webBuilder.Build();
app.UseStaticFiles();
app.Run();
<!-- wwwroot/index.html -->
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>SignalR 채팅</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
padding-top: 50px;
}
#messageContainer {
height: 400px;
overflow-y: scroll;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1 class="mt-5">SignalR 채팅</h1>
<div id="messageContainer"></div>
<input type="text" id="userNameInput" class="form-control mt-2" placeholder="이름을 입력하세요">
<input type="text" id="messageInput" class="form-control mt-2" placeholder="메시지를 입력하세요...">
<button id="transmitButton" class="btn btn-primary mt-2">전송</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.min.js"></script>
<script>
const hubConnection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:5000/chatHub", { withCredentials: true }) // 자격 증명 전송 허용
.configureLogging(signalR.LogLevel.Information)
.build();
// 연결 시작
hubConnection.start().then(() => {
console.log("SignalR 연결이 설정되었습니다");
}).catch(err => console.error(err.toString()));
// 메시지 수신
hubConnection.on("ReceiveMessage", (user, message) => {
const messageElement = document.createElement("div");
messageElement.textContent = `${user}: ${message}`;
document.getElementById("messageContainer").appendChild(messageElement);
window.scrollTo(0, document.body.scrollHeight);
});
// 메시지 전송
document.getElementById("transmitButton").addEventListener("click", async () => {
const user = document.getElementById("userNameInput").value;
const message = document.getElementById("messageInput").value;
await hubConnection.invoke("TransmitMessage", user, message);
document.getElementById("messageInput").value = "";
});
</script>
</body>
</html>
실시간 푸시 기능
public class BackgroundTask : BackgroundService
{
private readonly ILogger<BackgroundTask> _logger;
private readonly IHubContext<ChatHub> _hubContext;
public BackgroundTask(ILogger<BackgroundTask> logger, IHubContext<ChatHub> hubContext)
{
_logger = logger;
_hubContext = hubContext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("백그라운드 작업 실행 시간: {time}", DateTimeOffset.Now);
await _hubContext.Clients.All.SendAsync("OnlineUsers", "시스템", $"현재 연결된 사용자: {string.Join(',', ChatHub.ConnectedConnections.Keys)}");
await Task.Delay(10000, stoppingToken);
}
}
}
builder.Services.AddHostedService<BackgroundTask>();
간단한 기능 확인
클라이언트가 서버에 메시지를 보내면 서버가 즉시 응답합니다. 특정 클라이언트에게 응답하려면 SendAsync의 첫 번째 매개변수인 methodName을 수정하기만 하면 됩니다.
서버가 주기적으로 모든 클라이언트에게 현재 온라인 상태를 푸시합니다
SignalR 콘솔 클라이언트 예제
소스 코드 주소: https://gitee.com/xiaoqingyao/keep-alive-http-demo.git