Setup:
- SignalRServer console app: Microsoft.AspNet.SignalR.SelfHost v2.0.3
- SignalRClient console app: Microsoft.AspNet.SignalR.Client v2.0.3
- .NET 4.5.1
I do the following:
- Hit enter on client, a message is received at server and on client again
- Dispose the server by hitting any key in the server console
- Restart the server by hitting any key in the server console
- Client is reconnected
- Hit enter on client, message is received at server, but never reaches the client again
I expect the message to be received at the client again. I suspect it has something to do with the self-hosting, since I've tried to run the client against same hub in a web application running IIS with success.
Any ideas?
Update: if the server console process is killed and restarted, it is able to reconnect AND retrieve messages again.
Server code
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Hosting;
using Owin;
namespace SignalRServer
{
internal class Program
{
private static void Main(string[] args)
{
const string url = "http://localhost:8081";
while (true)
{
using (WebApp.Start<Startup>(url))
{
Console.WriteLine("Server running on {0}. Hit any key to stop.", url);
Console.ReadLine();
}
Console.WriteLine("Server stopped");
Console.WriteLine("Hit any key to restart, Esc to exit");
ConsoleKeyInfo ki = Console.ReadKey(true);
if (ki.Key == ConsoleKey.Escape)
return;
}
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
public class AuthenticationHub : Hub
{
public void BroadcastMessageToAll(string message)
{
Clients.All.sendMessageToClient(message);
Console.WriteLine("sendMessageToClient: " + message);
}
public override Task OnConnected()
{
Console.WriteLine("OnConnected " + Context.ConnectionId);
return base.OnConnected();
}
public override Task OnReconnected()
{
Console.WriteLine("OnReconnected " + Context.ConnectionId);
return base.OnReconnected();
}
public override Task OnDisconnected()
{
Console.WriteLine("OnDisconnected " + Context.ConnectionId);
return base.OnReconnected();
}
}
}
Client code
using System;
using Microsoft.AspNet.SignalR.Client;
namespace SignalRClient
{
class Program
{
static void Main(string[] args)
{
while (true)
{
var hubConnection = new HubConnection("http://localhost:8081/signalr/hubs");
hubConnection.Closed += () => Console.WriteLine("Closed");
hubConnection.StateChanged += e => Console.WriteLine("StateChanged: " + e.OldState + " " + e.NewState);
var hubProxy = hubConnection.CreateHubProxy("AuthenticationHub");
hubProxy.On<string>("sendMessageToClient",
info => Console.WriteLine("sendMessageToClient received: " + info));
hubConnection.Start();
Console.WriteLine("Client started - hit Enter to send a message - ESC to stop");
Console.ReadKey();
while (true)
{
var keyInfo = Console.ReadKey(true);
if (keyInfo.Key == ConsoleKey.Escape)
break;
var message = "Console client : " + DateTime.Now.ToString("HH:mm:ss-fff");
hubProxy.Invoke("BroadcastMessageToAll", message);
Console.WriteLine("Client sent BroadcastMessageToAll: " + message);
}
Console.WriteLine("Client stopping");
hubConnection.Stop();
Console.WriteLine("Client stopped - enter any key start again");
Console.ReadLine();
}
}
}
}
The SignalR team pointed me to the solution: By default SignalR uses GlobalHost, which is a singleton resolver. When disposed, it will never come back.
When creating the configuration for the hub, you should pass inn a new dependency resolver:
I has several similar problems with SignalR (not exacly in the same scenario).
The problem was usually caused in communication model request_from_server->client->response_to_server. When I tried to call Invoke in client code (to send response to server) directly in the receiving procedure, I got strange behavior (infinite waits, strange side effects, etc.).
Solution was to divide process into two threads. One for receiving messages from server to local ConcurrentQueue. The second thread fetches messages from the queue, processes them and sends responses back to server (by Invoke). Now SignalR works as expected.
This issue was probably caused by trying to send response BEFORE receiving procedure finished. This does not happen in usual client->server->client communication model.
Stian's answer is great. But if you need to use static methods defined in GlobalHost, you need to assign the Dependency resolver to GlobalHost.
The following works for me:
I defined the hub context this way: