다양한 유형의 연결 해제를 처리해야하는 모바일 애플리케이션에서 .NET 클라이언트와 함께 SignalR 2.0을 사용하고 있습니다. SignalR 클라이언트가 자동으로 다시 연결되는 경우도 있으며, 다시 호출 HubConnection.Start()
하여 직접 다시 연결해야하는 경우도 있습니다 .
SignalR이 가끔 마법처럼 자동으로 다시 연결되기 때문에 기능이나 구성 설정이 누락되었는지 궁금합니다.
자동으로 다시 연결되는 클라이언트를 설정하는 가장 좋은 방법은 무엇입니까?
Closed()
이벤트 를 처리하고 n 초 후에 연결 하는 자바 스크립트 예제를 보았습니다 . 권장되는 접근 방식이 있습니까?
내가 읽은 문서 와 SignalR 연결의 수명에 대한 여러 기사를,하지만 난 여전히 클라이언트 재 연결을 처리하는 방법에 대한 불분명 해요.
답변
나는 마침내 이것을 알아 냈다. 이 질문을 시작한 이후로 배운 내용은 다음과 같습니다.
배경 : Xamarin / Monotouch 및 .NET SignalR 2.0.3 클라이언트를 사용하여 iOS 앱을 빌드하고 있습니다. 우리는 기본 SignalR 프로토콜을 사용하고 있으며 웹 소켓 대신 SSE를 사용하는 것 같습니다. Xamarin / Monotouch에서 웹 소켓을 사용할 수 있는지 아직 확실하지 않습니다. 모든 것은 Azure 웹 사이트를 사용하여 호스팅됩니다.
SignalR 서버에 빠르게 다시 연결하려면 앱이 필요했지만 연결이 자체적으로 다시 연결되지 않거나 다시 연결하는 데 정확히 30 초가 소요되는 문제가 계속 발생했습니다 (기본 프로토콜 시간 초과로 인해).
테스트를 마친 세 가지 시나리오가 있습니다.
시나리오 A-앱이 처음로드 될 때 연결. 이것은 첫날부터 완벽하게 작동했습니다. 연결은 3G 모바일 연결에서도 0.25 초 이내에 완료됩니다. (이미 라디오가 켜져 있다고 가정)
시나리오 B-앱이 30 초 동안 유휴 / 닫힌 후 SignalR 서버에 다시 연결합니다. 이 시나리오에서 SignalR 클라이언트는 결국 특별한 작업없이 자체적으로 서버에 다시 연결되지만 다시 연결을 시도하기 전에 정확히 30 초 동안 대기하는 것처럼 보입니다. (우리 앱에 비해 너무 느림)
이 30 초의 대기 기간 동안 아무런 효과가 없었던 HubConnection.Start () 호출을 시도했습니다. HubConnection.Stop () 호출도 30 초가 걸립니다. SignalR 사이트에서 해결 된 것으로 보이는 관련 버그를 발견 했지만 v2.0.3에서도 여전히 동일한 문제가 발생합니다.
시나리오 C-앱이 120 초 이상 유휴 / 닫힌 후 SignalR 서버에 다시 연결합니다. 이 시나리오에서는 SignalR 전송 프로토콜이 이미 시간 초과되었으므로 클라이언트가 자동으로 다시 연결되지 않습니다. 이것은 클라이언트가 가끔씩 만 재 연결되는 이유를 설명합니다. 좋은 소식은 HubConnection.Start () 호출이 시나리오 A처럼 거의 즉시 작동한다는 것입니다.
그래서 앱이 30 초 동안 닫혔는지 120 초 이상으로 닫혔는지에 따라 재 연결 조건이 다르다는 것을 깨닫는 데 시간이 걸렸습니다. SignalR 추적 로그가 기본 프로토콜에서 진행되는 작업을 조명하지만 코드에서 전송 수준 이벤트를 처리 할 방법이 없다고 생각합니다. (Closed () 이벤트는 시나리오 B에서 30 초 후 시나리오 C에서 즉시 발생합니다. State 속성은 이러한 재 연결 대기 기간 동안 “연결됨”으로 표시됩니다. 다른 관련 이벤트 나 메서드는 없음)
해결책 :
해결책은 분명합니다. SignalR이 재 연결 마법을 수행하기를 기다리지 않습니다. 대신 앱이 활성화되거나 휴대 전화의 네트워크 연결이 복원 될 때 이벤트를 정리하고 HubConnection을 참조 해제합니다 (30 초가 걸리므로 삭제할 수 없습니다. 가비지 수집이 처리되기를 바랍니다. ) 및 새 인스턴스 생성. 이제 모든 것이 잘 작동합니다. 어떤 이유로 새 인스턴스를 만드는 대신 지속 연결을 다시 사용하고 다시 연결해야한다고 생각했습니다.
답변
연결이 끊긴 이벤트에 타이머를 설정하여 자동으로 다시 연결을 시도하는 것이 내가 아는 유일한 방법입니다.
자바 스크립트에서는 다음과 같이 수행됩니다.
$.connection.hub.disconnected(function() {
setTimeout(function() {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
이것은 문서에서 권장되는 접근 방식입니다.
답변
.NET 클라이언트를 요구하는 OP 이후 (아래의 winform 구현),
private async Task<bool> ConnectToSignalRServer()
{
bool connected = false;
try
{
Connection = new HubConnection("server url");
Hub = Connection.CreateHubProxy("MyHub");
await Connection.Start();
//See @Oran Dennison's comment on @KingOfHypocrites's answer
if (Connection.State == ConnectionState.Connected)
{
connected = true;
Connection.Closed += Connection_Closed;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
return connected;
}
private async void Connection_Closed()
{ // A global variable being set in "Form_closing" event
// of Form, check if form not closed explicitly to prevent a possible deadlock.
if(!IsFormClosed)
{
// specify a retry duration
TimeSpan retryDuration = TimeSpan.FromSeconds(30);
DateTime retryTill = DateTime.UtcNow.Add(retryDuration);
while (DateTime.UtcNow < retryTill)
{
bool connected = await ConnectToSignalRServer();
if (connected)
return;
}
Console.WriteLine("Connection closed")
}
}
답변
ibubi 답변에 대한 업데이트를 추가합니다 . 누군가 그것을 필요로 할 수도 있습니다. 어떤 경우에는 신호기가 다시 연결이 중지 된 후 “닫힌”이벤트가 발생하지 않는 것을 발견했습니다. 이벤트 “StateChanged”를 사용하여 해결했습니다. SignalR 서버에 연결하는 방법 :
private async Task<bool> ConnectToSignalRServer()
{
bool connected = false;
try
{
var connection = new HubConnection(ConnectionUrl);
var proxy = connection.CreateHubProxy("CurrentData");
await connection.Start();
if (connection.State == ConnectionState.Connected)
{
await proxy.Invoke("ConnectStation");
connection.Error += (ex) =>
{
Console.WriteLine("Connection error: " + ex.ToString());
};
connection.Closed += () =>
{
Console.WriteLine("Connection closed");
};
connection.StateChanged += Connection_StateChanged;
Console.WriteLine("Server for Current is started.");
connected = true;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
return connected;
}
다시 연결하는 방법 :
private async void Connection_StateChanged(StateChange obj)
{
if (obj.NewState == ConnectionState.Disconnected)
{
await RestartConnection();
}
}
서버에 연결을 끊임없이 시도하는 방법 (또한이 방법을 사용하여 첫 번째 연결을 생성) :
public async Task RestartConnection()
{
while (!ApplicationClosed)
{
bool connected = await ConnectToSignalRServer();
if (connected)
return;
}
}
답변
매직 재 연결 문제를 방지하기 위해 재 연결 상태가 시작되기 전에 안드로이드에서 서버 메소드를 호출 할 수 있습니다.
SignalR 허브 C #
public class MyHub : Hub
{
public void Ping()
{
//ping for android long polling
}
}
Android에서
private final int PING_INTERVAL = 10 * 1000;
private boolean isConnected = false;
private HubConnection connection;
private ClientTransport transport;
private HubProxy hubProxy;
private Handler handler = new Handler();
private Runnable ping = new Runnable() {
@Override
public void run() {
if (isConnected) {
hubProxy.invoke("ping");
handler.postDelayed(ping, PING_INTERVAL);
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
System.setProperty("http.keepAlive", "false");
.....
.....
connection.connected(new Runnable() {
@Override
public void run() {
System.out.println("Connected");
handler.postDelayed(ping, PING_INTERVAL);
});
}