ASP.NET SignalR Hubs API 가이드 - 서버(SignalR 1.x)
작성자: Patrick Fletcher, Tom Dykstra
경고
이 설명서는 최신 버전의 SignalR용이 아닙니다. ASP.NET Core SignalR을 살펴보세요.
이 문서에서는 일반적인 옵션을 보여 주는 코드 샘플과 함께 ASP.NET SignalR Hubs API for SignalR 버전 1.1의 서버 쪽을 프로그래밍하는 방법을 소개합니다.
SignalR Hubs API를 사용하면 서버에서 연결된 클라이언트로, 클라이언트에서 서버로 RPC(원격 프로시저 호출)를 수행할 수 있습니다. 서버 코드에서는 클라이언트에서 호출할 수 있는 메서드를 정의하고 클라이언트에서 실행되는 메서드를 호출합니다. 클라이언트 코드에서는 서버에서 호출할 수 있는 메서드를 정의하고 서버에서 실행되는 메서드를 호출합니다. SignalR은 모든 클라이언트-서버 배관을 처리합니다.
또한 SignalR은 영구 연결이라는 하위 수준 API를 제공합니다. SignalR, Hubs 및 영구 연결에 대한 소개 또는 전체 SignalR 애플리케이션을 빌드하는 방법을 보여 주는 자습서는 SignalR - 시작 참조하세요.
개요
이 문서는 다음 섹션으로 구성됩니다.
클라이언트를 프로그래밍하는 방법에 대한 설명서는 다음 리소스를 참조하세요.
API 참조 topics 대한 링크는 .NET 4.5 버전의 API에 대한 링크입니다. .NET 4를 사용하는 경우 API topics .NET 4 버전을 참조하세요.
SignalR 경로를 등록하고 SignalR 옵션을 구성하는 방법
클라이언트가 허브에 연결하는 데 사용할 경로를 정의하려면 애플리케이션이 시작될 때 MapHubs 메서드를 호출합니다. MapHubs
는 클래스의 확장 메서드 입니다 System.Web.Routing.RouteCollection
. 다음 예제에서는 Global.asax 파일에서 SignalR Hubs 경로를 정의하는 방법을 보여 줍니다.
using System.Web.Routing;
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs();
}
}
ASP.NET MVC 애플리케이션에 SignalR 기능을 추가하는 경우 SignalR 경로가 다른 경로 앞에 추가되었는지 확인합니다. 자세한 내용은 자습서: SignalR 및 MVC 4를 사용한 시작 참조하세요.
The /signalr URL
기본적으로 클라이언트가 허브에 연결하는 데 사용할 경로 URL은 "/signalr"입니다. (이 URL을 자동으로 생성된 JavaScript 파일에 대한 "/signalr/hubs" URL과 혼동하지 마세요. 생성된 프록시에 대한 자세한 내용은 SignalR Hubs API 가이드 - JavaScript 클라이언트 - 생성된 프록시 및 사용자가 수행하는 작업을 참조하세요.)
이 기본 URL을 SignalR에 사용할 수 없게 만드는 특별한 상황이 있을 수 있습니다. 예를 들어 프로젝트에 signalr 라는 폴더가 있으며 이름을 변경하지 않으려는 경우 이 경우 다음 예제와 같이 기본 URL을 변경할 수 있습니다(샘플 코드의 "/signalr"를 원하는 URL로 바꿉니다).
URL을 지정하는 서버 코드
RouteTable.Routes.MapHubs("/signalr", new HubConfiguration());
URL을 지정하는 JavaScript 클라이언트 코드(생성된 프록시 사용)
$.connection.hub.url = "/signalr"
$.connection.hub.start().done(init);
URL을 지정하는 JavaScript 클라이언트 코드(생성된 프록시 제외)
var connection = $.hubConnection("/signalr", { useDefaultPath: false });
URL을 지정하는 .NET 클라이언트 코드
var hubConnection = new HubConnection("http://contoso.com/signalr", useDefaultUrl: false);
SignalR 옵션 구성
메서드의 MapHubs
오버로드를 사용하면 사용자 지정 URL, 사용자 지정 종속성 확인자 및 다음 옵션을 지정할 수 있습니다.
브라우저 클라이언트에서 도메인 간 호출을 사용하도록 설정합니다.
일반적으로 브라우저가 에서
http://contoso.com
페이지를 로드하는 경우 SignalR 연결은 의 동일한 도메인에 있습니다http://contoso.com/signalr
. 의 페이지http://contoso.com
가 에 연결http://fabrikam.com/signalr
하면 도메인 간 연결입니다. 보안상의 이유로 도메인 간 연결은 기본적으로 사용하지 않도록 설정됩니다. 자세한 내용은 ASP.NET SignalR Hubs API 가이드 - JavaScript 클라이언트 - 도메인 간 연결을 설정하는 방법을 참조하세요.자세한 오류 메시지를 사용하도록 설정합니다.
오류가 발생하면 SignalR의 기본 동작은 발생한 작업에 대한 세부 정보 없이 클라이언트에 알림 메시지를 보내는 것입니다. 악의적인 사용자가 애플리케이션에 대한 공격에 정보를 사용할 수 있으므로 프로덕션 환경에서는 클라이언트에 자세한 오류 정보를 보내지 않는 것이 좋습니다. 문제 해결을 위해 이 옵션을 사용하여 더 많은 정보를 제공하는 오류 보고를 일시적으로 사용하도록 설정할 수 있습니다.
자동으로 생성된 JavaScript 프록시 파일을 사용하지 않도록 설정합니다.
기본적으로 허브 클래스에 대한 프록시가 있는 JavaScript 파일은 URL "/signalr/hubs"에 대한 응답으로 생성됩니다. JavaScript 프록시를 사용하지 않으려는 경우 또는 이 파일을 수동으로 생성하고 클라이언트의 실제 파일을 참조하려는 경우 이 옵션을 사용하여 프록시 생성을 사용하지 않도록 설정할 수 있습니다. 자세한 내용은 SignalR Hubs API 가이드 - JavaScript 클라이언트 - SignalR 생성 프록시에 대한 물리적 파일을 만드는 방법을 참조하세요.
다음 예제에서는 메서드에 대한 호출에서 SignalR 연결 URL 및 이러한 옵션을 지정하는 MapHubs
방법을 보여줍니다. 사용자 지정 URL을 지정하려면 예제의 "/signalr"를 사용하려는 URL로 바꿉니다.
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableCrossDomain = true;
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);
허브 클래스를 만들고 사용하는 방법
허브를 만들려면 Microsoft.Aspnet.Signalr.Hub에서 파생되는 클래스를 만듭니다. 다음 예제에서는 채팅 애플리케이션에 대한 간단한 허브 클래스를 보여 줍니다.
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
이 예제에서 연결된 클라이언트는 메서드를 NewContosoChatMessage
호출할 수 있으며, 메서드를 호출하면 수신된 데이터가 연결된 모든 클라이언트에 브로드캐스트됩니다.
허브 개체 수명
허브 클래스를 인스턴스화하거나 서버의 자체 코드에서 해당 메서드를 호출하지 않습니다. SignalR Hubs 파이프라인에서 수행하는 모든 작업입니다. SignalR은 클라이언트가 서버에 연결하거나, 연결을 끊거나, 메서드를 호출할 때와 같은 허브 작업을 처리해야 할 때마다 허브 클래스의 새 instance 만듭니다.
허브 클래스의 인스턴스는 일시적이므로 한 메서드 호출에서 다음 메서드 호출로 상태를 유지하는 데 사용할 수 없습니다. 서버가 클라이언트에서 메서드 호출을 받을 때마다 허브 클래스의 새 instance 메시지를 처리합니다. 여러 연결 및 메서드 호출을 통해 상태를 유지하려면 데이터베이스와 같은 다른 메서드나 허브 클래스의 정적 변수 또는 에서 Hub
파생되지 않는 다른 클래스를 사용합니다. 허브 클래스의 정적 변수와 같은 메서드를 사용하여 메모리에 데이터를 유지하면 앱 도메인이 재활용될 때 데이터가 손실됩니다.
Hub 클래스 외부에서 실행되는 사용자 고유의 코드에서 클라이언트에 메시지를 보내려면 Hub 클래스 instance 인스턴스화하여 메시지를 보낼 수 없지만 Hub 클래스에 대한 SignalR 컨텍스트 개체에 대한 참조를 가져오면 메시지를 수행할 수 있습니다. 자세한 내용은 이 항목의 뒷부분에 있는 허브 클래스 외부에서 클라이언트 메서드를 호출하고 그룹을 관리하는 방법을 참조하세요.
JavaScript 클라이언트의 허브 이름 낙타 대/소문자
기본적으로 JavaScript 클라이언트는 클래스 이름의 카멜 대/소문자 버전을 사용하여 허브를 참조합니다. SignalR은 JavaScript 코드가 JavaScript 규칙을 준수할 수 있도록 이 변경을 자동으로 수행합니다. 이전 예제를 JavaScript 코드에서 라고 contosoChatHub
합니다.
서버
public class ContosoChatHub : Hub
생성된 프록시를 사용하는 JavaScript 클라이언트
var contosoChatHubProxy = $.connection.contosoChatHub;
클라이언트에서 사용할 다른 이름을 지정하려면 특성을 추가 HubName
합니다. 특성을 사용하는 HubName
경우 JavaScript 클라이언트에서 카멜 대/소문자가 변경되지 않습니다.
서버
[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub
생성된 프록시를 사용하는 JavaScript 클라이언트
var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;
다중 허브
애플리케이션에서 여러 허브 클래스를 정의할 수 있습니다. 이렇게 하면 연결이 공유되지만 그룹은 별개입니다.
모든 클라이언트는 동일한 URL을 사용하여 서비스와 SignalR 연결("/signalr" 또는 사용자 지정 URL을 지정한 경우)을 설정하며, 해당 연결은 서비스에서 정의한 모든 허브에 사용됩니다.
단일 클래스에서 모든 허브 기능을 정의하는 것과 비교할 때 여러 Hubs의 성능 차이는 없습니다.
모든 허브는 동일한 HTTP 요청 정보를 가져옵니다.
모든 허브는 동일한 연결을 공유하므로 서버가 가져오는 유일한 HTTP 요청 정보는 SignalR 연결을 설정하는 원래 HTTP 요청에 제공되는 것입니다. 연결 요청을 사용하여 쿼리 문자열을 지정하여 클라이언트에서 서버로 정보를 전달하는 경우 다른 허브에 다른 쿼리 문자열을 제공할 수 없습니다. 모든 허브는 동일한 정보를 받습니다.
생성된 JavaScript 프록시 파일에는 하나의 파일에 있는 모든 Hubs에 대한 프록시가 포함됩니다.
JavaScript 프록시에 대한 자세한 내용은 SignalR Hubs API 가이드 - JavaScript 클라이언트 - 생성된 프록시 및 사용자가 수행하는 작업을 참조하세요.
그룹은 허브 내에서 정의됩니다.
SignalR에서 연결된 클라이언트의 하위 집합으로 브로드캐스트할 명명된 그룹을 정의할 수 있습니다. 그룹은 각 허브에 대해 별도로 유지 관리됩니다. 예를 들어 "Administrators"라는 그룹에는 클래스에 대한
ContosoChatHub
하나의 클라이언트 집합이 포함되며, 동일한 그룹 이름은 클래스에 대해StockTickerHub
다른 클라이언트 집합을 참조합니다.
클라이언트가 호출할 수 있는 허브 클래스에서 메서드를 정의하는 방법
클라이언트에서 호출할 수 있게 하려는 허브에 메서드를 노출하려면 다음 예제와 같이 공용 메서드를 선언합니다.
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
public class StockTickerHub : Hub
{
public IEnumerable<Stock> GetAllStocks()
{
return _stockTicker.GetAllStocks();
}
}
C# 메서드에서와 마찬가지로 복합 형식 및 배열을 포함한 반환 형식 및 매개 변수를 지정할 수 있습니다. 매개 변수에서 수신하거나 호출자에게 반환되는 모든 데이터는 JSON을 사용하여 클라이언트와 서버 간에 전달되고 SignalR은 복잡한 개체 및 개체 배열의 바인딩을 자동으로 처리합니다.
JavaScript 클라이언트에서 메서드 이름의 카멜식 대/소문자
기본적으로 JavaScript 클라이언트는 메서드 이름의 카멜식 대/소문자 버전을 사용하여 허브 메서드를 참조합니다. SignalR은 JavaScript 코드가 JavaScript 규칙을 준수할 수 있도록 이 변경을 자동으로 수행합니다.
서버
public void NewContosoChatMessage(string userName, string message)
생성된 프록시를 사용하는 JavaScript 클라이언트
contosoChatHubProxy.server.newContosoChatMessage($(userName, message);
클라이언트에서 사용할 다른 이름을 지정하려면 특성을 추가 HubMethodName
합니다.
서버
[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)
생성된 프록시를 사용하는 JavaScript 클라이언트
contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);
비동기적으로 실행해야 하는 경우
메서드가 장기 실행되거나 데이터베이스 조회 또는 웹 서비스 호출과 같이 대기와 관련된 작업을 수행해야 하는 경우 반환 형식 대신 void
T
Task(반환 대신) Task<T> 개체를 반환하여 Hub 메서드를 비동기식으로 만듭니다. 메서드에서 개체를 Task
반환하는 경우 SignalR은 가 Task
완료되기를 기다린 다음 래핑되지 않은 결과를 클라이언트로 다시 보내므로 클라이언트에서 메서드 호출을 코딩하는 방법에는 차이가 없습니다.
Hub 메서드를 비동기식으로 설정하면 WebSocket 전송을 사용할 때 연결이 차단되는 것을 방지할 수 있습니다. Hub 메서드가 동기적으로 실행되고 전송이 WebSocket이면 허브 메서드가 완료될 때까지 동일한 클라이언트에서 허브에 대한 메서드의 후속 호출이 차단됩니다.
다음 예제에서는 동기 또는 비동기적으로 실행하도록 코딩된 동일한 메서드와 두 버전 중 하나를 호출하는 데 작동하는 JavaScript 클라이언트 코드를 보여 줍니다.
동기
public IEnumerable<Stock> GetAllStocks()
{
// Returns data from memory.
return _stockTicker.GetAllStocks();
}
비동기 - ASP.NET 4.5
public async Task<IEnumerable<Stock>> GetAllStocks()
{
// Returns data from a web service.
var uri = Util.getServiceUri("Stocks");
using (HttpClient httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(uri);
return (await response.Content.ReadAsAsync<IEnumerable<Stock>>());
}
}
생성된 프록시를 사용하는 JavaScript 클라이언트
stockTickerHubProxy.server.getAllStocks().done(function (stocks) {
$.each(stocks, function () {
alert(this.Symbol + ' ' + this.Price);
});
});
ASP.NET 4.5에서 비동기 메서드를 사용하는 방법에 대한 자세한 내용은 ASP.NET MVC 4에서 비동기 메서드 사용을 참조하세요.
오버로드 정의
메서드에 대한 오버로드를 정의하려면 각 오버로드의 매개 변수 수가 달라야 합니다. 다른 매개 변수 형식을 지정하여 오버로드를 구분하는 경우 허브 클래스가 컴파일되지만 클라이언트가 오버로드 중 하나를 호출하려고 할 때 SignalR 서비스는 런타임에 예외를 throw합니다.
허브 클래스에서 클라이언트 메서드를 호출하는 방법
서버에서 클라이언트 메서드를 호출하려면 허브 클래스의 Clients
메서드에서 속성을 사용합니다. 다음 예제에서는 연결된 모든 클라이언트에서 를 호출 addNewMessageToPage
하는 서버 코드와 JavaScript 클라이언트에서 메서드를 정의하는 클라이언트 코드를 보여 줍니다.
서버
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
생성된 프록시를 사용하는 JavaScript 클라이언트
contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '<li>');
};
클라이언트 메서드에서 반환 값을 가져올 수 없습니다. 와 같은 int x = Clients.All.add(1,1)
구문이 작동하지 않습니다.
매개 변수에 대해 복합 형식 및 배열을 지정할 수 있습니다. 다음 예제에서는 메서드 매개 변수의 클라이언트에 복합 형식을 전달합니다.
복잡한 개체를 사용하여 클라이언트 메서드를 호출하는 서버 코드
public void SendMessage(string name, string message)
{
Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}
복합 개체를 정의하는 서버 코드
public class ContosoChatMessage
{
public string UserName { get; set; }
public string Message { get; set; }
}
생성된 프록시를 사용하는 JavaScript 클라이언트
var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
console.log(message.UserName + ' ' + message.Message);
});
RPC를 받을 클라이언트 선택
Clients 속성은 RPC를 받을 클라이언트를 지정하는 몇 가지 옵션을 제공하는 HubConnectionContext 개체를 반환합니다.
연결된 모든 클라이언트입니다.
Clients.All.addContosoChatMessageToPage(name, message);
호출 클라이언트만 해당합니다.
Clients.Caller.addContosoChatMessageToPage(name, message);
호출 클라이언트를 제외한 모든 클라이언트.
Clients.Others.addContosoChatMessageToPage(name, message);
연결 ID로 식별되는 특정 클라이언트입니다.
Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
이 예제에서는 호출 클라이언트에서 를 호출
addContosoChatMessageToPage
하고 를 사용하는Clients.Caller
것과 동일한 효과를 입니다.지정된 클라이언트를 제외한 모든 연결된 클라이언트는 연결 ID로 식별됩니다.
Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
지정된 그룹의 연결된 모든 클라이언트입니다.
Clients.Group(groupName).addContosoChatMessageToPage(name, message);
지정된 클라이언트를 제외한 지정된 그룹의 모든 연결된 클라이언트는 연결 ID로 식별됩니다.
Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
호출 클라이언트를 제외한 지정된 그룹의 연결된 모든 클라이언트입니다.
Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);
메서드 이름에 대한 컴파일 시간 유효성 검사 없음
지정하는 메서드 이름은 동적 개체로 해석됩니다. 즉, IntelliSense 또는 컴파일 시간 유효성 검사가 없습니다. 식은 런타임에 계산됩니다. 메서드 호출이 실행되면 SignalR은 메서드 이름과 매개 변수 값을 클라이언트에 보내고 클라이언트에 이름과 일치하는 메서드가 있는 경우 해당 메서드가 호출되고 매개 변수 값이 전달됩니다. 클라이언트에서 일치하는 메서드를 찾을 수 없으면 오류가 발생하지 않습니다. 클라이언트 메서드를 호출할 때 SignalR이 백그라운드에서 클라이언트로 전송하는 데이터의 형식에 대한 자세한 내용은 SignalR 소개를 참조하세요.
대/소문자를 구분하지 않는 메서드 이름 일치
메서드 이름 일치는 대/소문자를 구분하지 않습니다. 예를 들어 Clients.All.addContosoChatMessageToPage
서버에서 클라이언트에서 , addcontosochatmessagetopage
또는 addContosoChatMessageToPage
를 실행AddContosoChatMessageToPage
합니다.
비동기 실행
호출하는 메서드는 비동기적으로 실행됩니다. 클라이언트에 대한 메서드 호출 후에 오는 모든 코드는 후속 코드 줄이 메서드 완료를 기다려야 한다고 지정하지 않는 한 SignalR이 클라이언트에 데이터 전송을 완료할 때까지 기다리지 않고 즉시 실행됩니다. 다음 코드 예제에서는 두 개의 클라이언트 메서드를 순차적으로 실행하는 방법을 보여 줍니다. 하나는 .NET 4.5에서 작동하는 코드를 사용하고 다른 하나는 .NET 4에서 작동하는 코드를 사용합니다.
.NET 4.5 예제
async public Task NewContosoChatMessage(string name, string message)
{
await Clients.Others.addContosoChatMessageToPage(data);
Clients.Caller.notifyMessageSent();
}
.NET 4 예제
public void NewContosoChatMessage(string name, string message)
{
(Clients.Others.addContosoChatMessageToPage(data) as Task).ContinueWith(antecedent =>
Clients.Caller.notifyMessageSent());
}
또는 ContinueWith
를 사용하여 await
다음 코드 줄이 실행되기 전에 클라이언트 메서드가 완료될 때까지 대기하는 경우 다음 코드 줄이 실행되기 전에 클라이언트가 실제로 메시지를 수신한다는 의미는 아닙니다. 클라이언트 메서드 호출의 "완료"는 SignalR이 메시지를 보내는 데 필요한 모든 작업을 수행했음을 의미합니다. 클라이언트가 메시지를 받았는지 확인해야 하는 경우 해당 메커니즘을 직접 프로그래밍해야 합니다. 예를 들어 허브에서 메서드를 MessageReceived
코딩할 수 있으며 클라이언트에서 addContosoChatMessageToPage
수행해야 하는 작업을 수행한 후 클라이언트의 메서드에서 를 호출 MessageReceived
할 수 있습니다. 허브에서 MessageReceived
실제 클라이언트 수신 및 원래 메서드 호출 처리에 따라 어떤 작업이든 수행할 수 있습니다.
문자열 변수를 메서드 이름으로 사용하는 방법
문자열 변수를 메서드 이름으로 사용하여 클라이언트 메서드를 호출하려면 에 캐스팅 Clients.All
(또는 Clients.Others
, Clients.Caller
등) IClientProxy
한 다음 Invoke(methodName, args...)를 호출합니다.
public void NewContosoChatMessage(string name, string message)
{
string methodToCall = "addContosoChatMessageToPage";
IClientProxy proxy = Clients.All;
proxy.Invoke(methodToCall, name, message);
}
허브 클래스에서 그룹 멤버 자격을 관리하는 방법
SignalR의 그룹은 연결된 클라이언트의 지정된 하위 집합에 메시지를 브로드캐스트하는 방법을 제공합니다. 그룹에는 임의의 수의 클라이언트가 있을 수 있으며 클라이언트는 임의의 수의 그룹의 구성원일 수 있습니다.
그룹 멤버 자격을 관리하려면 허브 클래스의 속성에서 Groups
제공하는 추가 및 제거 메서드를 사용합니다. 다음 예제에서는 클라이언트 코드에서 호출되는 Hub 메서드에 사용되는 및 Groups.Remove
메서드와 이를 호출하는 JavaScript 클라이언트 코드를 보여 Groups.Add
줍니다.
서버
public class ContosoChatHub : Hub
{
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
}
생성된 프록시를 사용하는 JavaScript 클라이언트
contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);
그룹을 명시적으로 만들 필요가 없습니다. 실제로 에 대한 호출 Groups.Add
에서 이름을 처음 지정할 때 그룹이 자동으로 만들어지고 멤버 자격에서 마지막 연결을 제거하면 그룹이 삭제됩니다.
그룹 멤버 자격 목록 또는 그룹 목록을 가져오기 위한 API는 없습니다. SignalR은 pub/sub 모델을 기반으로 클라이언트 및 그룹에 메시지를 보내고 서버는 그룹 또는 그룹 멤버 자격 목록을 유지 관리하지 않습니다. 이는 웹 팜에 노드를 추가할 때마다 SignalR이 유지 관리하는 모든 상태를 새 노드로 전파해야 하기 때문에 확장성을 최대화하는 데 도움이 됩니다.
메서드 추가 및 제거의 비동기 실행
및 Groups.Remove
메서드는 Groups.Add
비동기적으로 실행됩니다. 그룹에 클라이언트를 추가하고 그룹을 사용하여 클라이언트에 메시지를 즉시 보내려면 메서드가 Groups.Add
먼저 완료되었는지 확인해야 합니다. 다음 코드 예제에서는 .NET 4.5에서 작동하는 코드를 사용하고 다른 하나는 .NET 4에서 작동하는 코드를 사용하여 수행하는 방법을 보여 줍니다.
.NET 4.5 예제
async public Task JoinGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
}
.NET 4 예제
public void JoinGroup(string groupName)
{
(Groups.Add(Context.ConnectionId, groupName) as Task).ContinueWith(antecedent =>
Clients.Group(groupName).addContosoChatMessageToPage(Context.ConnectionId + " added to group"));
}
그룹 멤버 자격 지속성
SignalR은 사용자가 아닌 연결을 추적하므로 사용자가 연결을 설정할 때마다 사용자가 동일한 그룹에 연결되도록 하려면 사용자가 새 연결을 설정할 때마다 를 호출 Groups.Add
해야 합니다.
일시적으로 연결이 끊어지면 SignalR이 연결을 자동으로 복원할 수 있습니다. 이 경우 SignalR은 새 연결을 설정하지 않고 동일한 연결을 복원하므로 클라이언트의 그룹 멤버 자격이 자동으로 복원됩니다. 일시적인 중단이 서버 재부팅 또는 실패의 결과인 경우에도 발생할 수 있습니다. 그룹 멤버 자격을 포함하여 각 클라이언트의 연결 상태가 클라이언트에 라운드트립되기 때문입니다. 연결 시간이 초과되기 전에 서버가 다운되고 새 서버로 대체되는 경우 클라이언트는 새 서버에 자동으로 다시 연결하고 해당 서버가 속한 그룹에 다시 등록할 수 있습니다.
연결이 끊긴 후 또는 연결 시간이 초과되거나 클라이언트 연결이 끊어질 때(예: 브라우저가 새 페이지로 이동하는 경우) 연결을 자동으로 복원할 수 없는 경우 그룹 멤버 자격이 손실됩니다. 다음에 사용자가 연결할 때 새 연결이 됩니다. 동일한 사용자가 새 연결을 설정할 때 그룹 멤버 자격을 유지하려면 애플리케이션에서 사용자와 그룹 간의 연결을 추적하고 사용자가 새 연결을 설정할 때마다 그룹 멤버 자격을 복원해야 합니다.
연결 및 다시 연결에 대한 자세한 내용은 이 항목의 뒷부분에 있는 허브 클래스에서 연결 수명 이벤트를 처리하는 방법을 참조하세요.
단일 사용자 그룹
SignalR을 사용하는 애플리케이션은 일반적으로 메시지를 보낸 사용자와 메시지를 받아야 하는 사용자를 알기 위해 사용자와 연결 간의 연결을 추적해야 합니다. 그룹은 일반적으로 사용되는 두 가지 패턴 중 하나에서 사용됩니다.
단일 사용자 그룹.
사용자 이름을 그룹 이름으로 지정하고 사용자가 연결하거나 다시 연결할 때마다 현재 연결 ID를 그룹에 추가할 수 있습니다. 사용자에게 메시지를 보내려면 그룹에 보냅니다. 이 방법의 단점은 그룹이 사용자가 온라인인지 오프라인인지 확인하는 방법을 제공하지 않는다는 것입니다.
사용자 이름과 연결 ID 간의 연결을 추적합니다.
각 사용자 이름과 하나 이상의 연결 ID 간의 연결을 사전 또는 데이터베이스에 저장하고 사용자가 연결하거나 연결을 끊을 때마다 저장된 데이터를 업데이트할 수 있습니다. 사용자에게 메시지를 보내려면 연결 ID를 지정합니다. 이 메서드의 단점은 메모리가 더 많이 사용된다는 것입니다.
허브 클래스에서 연결 수명 이벤트를 처리하는 방법
연결 수명 이벤트를 처리하는 일반적인 이유는 사용자가 연결되어 있는지 여부를 추적하고 사용자 이름과 연결 ID 간의 연결을 추적하기 위해서입니다. 클라이언트가 연결하거나 연결을 끊을 때 사용자 고유의 코드를 실행하려면 다음 예제와 같이 허브 클래스의 , OnDisconnected
및 OnReconnected
가상 메서드를 재정OnConnected
의합니다.
public class ContosoChatHub : Hub
{
public override Task OnConnected()
{
// Add your own code here.
// For example: in a chat application, record the association between
// the current connection ID and user name, and mark the user as online.
// After the code in this method completes, the client is informed that
// the connection is established; for example, in a JavaScript client,
// the start().done callback is executed.
return base.OnConnected();
}
public override Task OnDisconnected()
{
// Add your own code here.
// For example: in a chat application, mark the user as offline,
// delete the association between the current connection id and user name.
return base.OnDisconnected();
}
public override Task OnReconnected()
{
// Add your own code here.
// For example: in a chat application, you might have marked the
// user as offline after a period of inactivity; in that case
// mark the user as online again.
return base.OnReconnected();
}
}
OnConnected, OnDisconnected 및 OnReconnected가 호출될 때
브라우저가 새 페이지로 이동할 때마다 새 연결이 설정되어야 합니다. 즉, SignalR이 메서드를 실행한 다음 메서드를 OnConnected
실행 OnDisconnected
합니다. SignalR은 새 연결이 설정될 때 항상 새 연결 ID를 만듭니다.
연결 OnReconnected
시간이 초과되기 전에 케이블의 연결이 일시적으로 끊어지고 다시 연결되는 경우와 같이 SignalR이 자동으로 복구할 수 있는 연결이 일시적으로 중단된 경우 메서드가 호출됩니다. 메서드는 OnDisconnected
클라이언트의 연결이 끊어지고 브라우저가 새 페이지로 이동하는 경우와 같이 SignalR이 자동으로 다시 연결할 수 없을 때 호출됩니다. 따라서 지정된 클라이언트에 대해 가능한 이벤트 시퀀스는 , , OnReconnected
OnDisconnected
또는 OnConnected
OnDisconnected
입니다OnConnected
. 지정된 연결에 대한 시퀀스 OnConnected
, 가 OnReconnected
OnDisconnected
표시되지 않습니다.
OnDisconnected
서버가 다운되거나 앱 도메인이 재활용되는 경우와 같은 일부 시나리오에서는 메서드가 호출되지 않습니다. 다른 서버가 줄을 서거나 앱 도메인이 재활용을 완료하면 일부 클라이언트가 이벤트를 다시 연결하고 발생시킬 OnReconnected
수 있습니다.
자세한 내용은 SignalR에서 연결 수명 이벤트 이해 및 처리를 참조하세요.
호출자 상태가 채워지지 않음
연결 수명 이벤트 처리기 메서드는 서버에서 호출됩니다. 즉, 클라이언트의 개체에 배치 state
한 모든 상태가 서버의 Caller
속성에 채워지지 않습니다. 개체 및 속성에 대한 자세한 내용은 이 항목의 state
Caller
뒷부분에 있는 클라이언트와 허브 클래스 간에 상태를 전달하는 방법을 참조하세요.
Context 속성에서 클라이언트에 대한 정보를 가져오는 방법
클라이언트에 대한 정보를 얻으려면 허브 클래스의 속성을 사용합니다 Context
. 속성은 Context
다음 정보에 대한 액세스를 제공하는 HubCallerContext 개체를 반환합니다.
호출 클라이언트의 연결 ID입니다.
string connectionID = Context.ConnectionId;
연결 ID는 SignalR에 의해 할당된 GUID입니다(사용자 고유의 코드에서 값을 지정할 수 없음). 각 연결에 대해 하나의 연결 ID가 있으며 애플리케이션에 여러 Hubs가 있는 경우 모든 Hubs에서 동일한 연결 ID를 사용합니다.
HTTP 헤더 데이터입니다.
System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
에서
Context.Headers
HTTP 헤더를 가져올 수도 있습니다. 동일한 항목에 대한 여러 참조가 먼저 생성되고 속성Context.Request
이Context.Headers
나중에Context.Headers
추가되었으며 이전 버전과의 호환성을 위해 유지되었기 때문입니다.문자열 데이터를 쿼리합니다.
System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString; string parameterValue = queryString["parametername"]
에서
Context.QueryString
쿼리 문자열 데이터를 가져올 수도 있습니다.이 속성에서 가져오는 쿼리 문자열은 SignalR 연결을 설정한 HTTP 요청과 함께 사용된 쿼리 문자열입니다. 클라이언트에서 서버로 클라이언트에 대한 데이터를 전달하는 편리한 방법인 연결을 구성하여 클라이언트에 쿼리 문자열 매개 변수를 추가할 수 있습니다. 다음 예제에서는 생성된 프록시를 사용할 때 JavaScript 클라이언트에서 쿼리 문자열을 추가하는 한 가지 방법을 보여줍니다.
$.connection.hub.qs = { "version" : "1.0" };
쿼리 문자열 매개 변수 설정에 대한 자세한 내용은 JavaScript 및 .NET 클라이언트에 대한 API 가이드를 참조하세요.
SignalR에서 내부적으로 사용되는 다른 값과 함께 쿼리 문자열 데이터의 연결에 사용되는 전송 메서드를 찾을 수 있습니다.
string transportMethod = queryString["transport"];
값
transportMethod
은 "webSockets", "serverSentEvents", "foreverFrame" 또는 "longPolling"입니다. 이벤트 처리기 메서드에서OnConnected
이 값을 검사 경우 일부 시나리오에서는 처음에 연결에 대한 최종 협상 전송 방법이 아닌 전송 값을 가져올 수 있습니다. 이 경우 메서드는 예외를 throw하고 최종 전송 메서드가 설정되면 나중에 다시 호출됩니다.쿠키.
System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
에서
Context.RequestCookies
쿠키를 가져올 수도 있습니다.사용자 정보입니다.
System.Security.Principal.IPrincipal user = Context.User;
요청에 대한 HttpContext 개체입니다.
System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();
SignalR 연결에 대한 개체를 가져오는
HttpContext.Current
HttpContext
대신 이 메서드를 사용합니다.
클라이언트와 허브 클래스 간에 상태를 전달하는 방법
클라이언트 프록시는 각 메서드 호출을 사용하여 서버로 전송하려는 데이터를 저장할 수 있는 개체를 제공합니다 state
. 서버에서 클라이언트에서 호출하는 허브 메서드의 Clients.Caller
속성에서 이 데이터에 액세스할 수 있습니다. Clients.Caller
연결 수명 이벤트 처리기 메서드 OnConnected
, OnDisconnected
및 OnReconnected
에 대한 속성이 채워지지 않습니다.
개체 및 속성에서 state
데이터를 만들거나 업데이트하는 Clients.Caller
작업은 양방향으로 작동합니다. 서버에서 값을 업데이트할 수 있으며 클라이언트에 다시 전달됩니다.
다음 예제에서는 모든 메서드 호출을 사용하여 서버로 전송할 상태를 저장하는 JavaScript 클라이언트 코드를 보여줍니다.
contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";
다음 예제에서는 .NET 클라이언트의 해당 코드를 보여 있습니다.
contosoChatHubProxy["userName"] = "Fadi Fakhouri";
chatHubProxy["computerName"] = "fadivm1";
허브 클래스의 속성에서 Clients.Caller
이 데이터에 액세스할 수 있습니다. 다음 예제에서는 이전 예제에서 참조된 상태를 검색하는 코드를 보여 줍니다.
public void NewContosoChatMessage(string data)
{
string userName = Clients.Caller.userName;
string computerName = Clients.Caller.computerName;
Clients.Others.addContosoChatMessageToPage(message, userName, computerName);
}
참고
상태를 유지하기 위한 이 메커니즘은 또는 Clients.Caller
속성에 state
입력한 모든 것이 모든 메서드 호출과 함께 라운드트립되므로 대량의 데이터에는 사용되지 않습니다. 사용자 이름 또는 카운터와 같은 작은 항목에 유용합니다.
허브 클래스에서 오류를 처리하는 방법
허브 클래스 메서드에서 발생하는 오류를 처리하려면 다음 메서드 중 하나 또는 둘 다를 사용합니다.
try-catch 블록에서 메서드 코드를 래핑하고 예외 개체를 기록합니다. 디버깅을 위해 예외를 클라이언트에 보낼 수 있지만 보안상의 이유로 프로덕션 환경에서 클라이언트에 자세한 정보를 보내는 것은 권장되지 않습니다.
OnIncomingError 메서드를 처리하는 Hubs 파이프라인 모듈을 만듭니다. 다음 예제에서는 오류를 기록하는 파이프라인 모듈과 모듈을 Hubs 파이프라인에 삽입하는 Global.asax의 코드를 보여 줍니다.
public class ErrorHandlingPipelineModule : HubPipelineModule { protected override void OnIncomingError(Exception ex, IHubIncomingInvokerContext context) { Debug.WriteLine("=> Exception " + ex.Message); if (ex.InnerException != null) { Debug.WriteLine("=> Inner Exception " + ex.InnerException.Message); } base.OnIncomingError(ex, context); } }
protected void Application_Start(object sender, EventArgs e) { GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); RouteTable.Routes.MapHubs(); }
허브 파이프라인 모듈에 대한 자세한 내용은 이 항목 의 뒷부분에 있는 Hubs 파이프라인을 사용자 지정하는 방법을 참조하세요.
추적을 사용하도록 설정하는 방법
서버 쪽 추적을 사용하도록 설정하려면 시스템을 추가합니다. 이 예제와 같이 요소를 Web.config 파일에 진단.
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit https://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="SignalRSamples" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;" />
<add name="SignalRSamplesWithMARS" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;MultipleActiveResultSets=true;" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
<system.diagnostics>
<sources>
<source name="SignalR.SqlMessageBus">
<listeners>
<add name="SignalR-Bus" />
</listeners>
</source>
<source name="SignalR.ServiceBusMessageBus">
<listeners>
<add name="SignalR-Bus" />
</listeners>
</source>
<source name="SignalR.ScaleoutMessageBus">
<listeners>
<add name="SignalR-Bus" />
</listeners>
</source>
<source name="SignalR.Transports.WebSocketTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.ServerSentEventsTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.ForeverFrameTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.LongPollingTransport">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
<source name="SignalR.Transports.TransportHeartBeat">
<listeners>
<add name="SignalR-Transports" />
</listeners>
</source>
</sources>
<switches>
<add name="SignalRSwitch" value="Verbose" />
</switches>
<sharedListeners>
<add name="SignalR-Transports"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="transports.log.txt" />
<add name="SignalR-Bus"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="bus.log.txt" />
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
</configuration>
Visual Studio에서 애플리케이션을 실행할 때 출력 창에서 로그를 볼 수 있습니다.
허브 클래스 외부에서 클라이언트 메서드를 호출하고 그룹을 관리하는 방법
허브 클래스와 다른 클래스에서 클라이언트 메서드를 호출하려면 허브에 대한 SignalR 컨텍스트 개체에 대한 참조를 가져와서 클라이언트에서 메서드를 호출하거나 그룹을 관리하는 데 사용합니다.
다음 샘플 StockTicker
클래스는 컨텍스트 개체를 가져오고, 클래스의 instance 저장하고, 클래스 instance 정적 속성에 저장하고, 싱글톤 클래스 instance 컨텍스트를 사용하여 라는 StockTickerHub
허브에 연결된 클라이언트에서 메서드를 호출 updateStockPrice
합니다.
// For the complete example, go to
// http://www.asp.net/signalr/overview/signalr-1x/getting-started/tutorial-server-broadcast-with-aspnet-signalr
// This sample only shows code related to getting and using the SignalR context.
public class StockTicker
{
// Singleton instance
private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>()));
private IHubContext _context;
private StockTicker(IHubContext context)
{
_context = context;
}
// This method is invoked by a Timer object.
private void UpdateStockPrices(object state)
{
foreach (var stock in _stocks.Values)
{
if (TryUpdateStockPrice(stock))
{
_context.Clients.All.updateStockPrice(stock);
}
}
}
수명이 긴 개체에서 컨텍스트를 여러 번 사용해야 하는 경우 매번 다시 가져오는 대신 참조를 한 번 가져와 저장합니다. 컨텍스트를 한 번 가져오면 SignalR이 허브 메서드가 클라이언트 메서드를 호출하는 것과 동일한 순서로 클라이언트에 메시지를 보냅니다. 허브에 SignalR 컨텍스트를 사용하는 방법을 보여 주는 자습서는 ASP.NET SignalR을 사용하여 서버 브로드캐스트를 참조하세요.
클라이언트 메서드 호출
RPC를 받을 클라이언트를 지정할 수 있지만 허브 클래스에서 를 호출할 때보다 더 적은 옵션이 있습니다. 그 이유는 컨텍스트가 클라이언트의 특정 호출과 연결되지 않았기 때문에 , 또는 또는 Clients.Caller
Clients.OthersInGroup
와 같은 Clients.Others
현재 연결 ID에 대한 지식이 필요한 메서드를 사용할 수 없기 때문입니다. 다음 옵션을 사용할 수 있습니다.
연결된 모든 클라이언트입니다.
context.Clients.All.addContosoChatMessageToPage(name, message);
연결 ID로 식별되는 특정 클라이언트입니다.
context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);
지정된 클라이언트를 제외한 모든 연결된 클라이언트는 연결 ID로 식별됩니다.
context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
지정된 그룹의 연결된 모든 클라이언트입니다.
context.Clients.Group(groupName).addContosoChatMessageToPage(name, message);
지정된 클라이언트를 제외한 지정된 그룹의 모든 연결된 클라이언트는 연결 ID로 식별됩니다.
Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
허브 클래스의 메서드에서 비 허브 클래스로 를 호출하는 경우 현재 연결 ID를 전달하고 , , Clients.AllExcept
또는 와 함께 Clients.Client
사용하여 , Clients.Others
또는 Clients.Group
Clients.OthersInGroup
를 시뮬레이션Clients.Caller
할 수 있습니다. 다음 예제에서 클래스는 MoveShapeHub
클래스가 를 시뮬레이션할 수 있도록 클래스에 Broadcaster
연결 ID를 Broadcaster
전달합니다 Clients.Others
.
// For the complete example, see
// http://www.asp.net/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr
// This sample only shows code that passes connection ID to the non-Hub class,
// in order to simulate Clients.Others.
public class MoveShapeHub : Hub
{
// Code not shown puts a singleton instance of Broadcaster in this variable.
private Broadcaster _broadcaster;
public void UpdateModel(ShapeModel clientModel)
{
clientModel.LastUpdatedBy = Context.ConnectionId;
// Update the shape model within our broadcaster
_broadcaster.UpdateShape(clientModel);
}
}
public class Broadcaster
{
public Broadcaster()
{
_hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
}
public void UpdateShape(ShapeModel clientModel)
{
_model = clientModel;
_modelUpdated = true;
}
// Called by a Timer object.
public void BroadcastShape(object state)
{
if (_modelUpdated)
{
_hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
_modelUpdated = false;
}
}
}
그룹 구성원 자격 관리
그룹을 관리하기 위해 허브 클래스에서와 동일한 옵션을 사용할 수 있습니다.
그룹에 클라이언트 추가
context.Groups.Add(connectionID, groupName);
그룹에서 클라이언트 제거
context.Groups.Remove(connectionID, groupName);
Hubs 파이프라인을 사용자 지정하는 방법
SignalR을 사용하면 허브 파이프라인에 사용자 고유의 코드를 삽입할 수 있습니다. 다음 예제에서는 클라이언트에서 수신된 각 들어오는 메서드 호출과 클라이언트에서 호출된 나가는 메서드 호출을 기록하는 사용자 지정 허브 파이프라인 모듈을 보여 줍니다.
public class LoggingPipelineModule : HubPipelineModule
{
protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context)
{
Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name);
return base.OnBeforeIncoming(context);
}
protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context)
{
Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub);
return base.OnBeforeOutgoing(context);
}
}
Global.asax 파일의 다음 코드는 허브 파이프라인에서 실행할 모듈을 등록합니다.
protected void Application_Start(object sender, EventArgs e)
{
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
RouteTable.Routes.MapHubs();
}
재정의할 수 있는 다양한 메서드가 있습니다. 전체 목록은 HubPipelineModule 메서드를 참조하세요.