aspnet / SignalR

[Archived] Incredibly simple real-time web for ASP.NET Core. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
2.38k stars 447 forks source link

Cannot start a connection that is not in the Initial state #969

Closed ghost closed 7 years ago

ghost commented 7 years ago

I have troubles to get my ASP.NET Core SignalR app working.

I have this server-side code :

public class PopcornHub : Hub
{
    private int Users;

    public async Task BroadcastNumberOfUsers(int nbUser)
    {
        await Clients.All.InvokeAsync("OnUserConnected", nbUser);
    }

    public override async Task OnConnectedAsync()
    {
        Users++;
        await BroadcastNumberOfUsers(Users);
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        Users--;
        await BroadcastNumberOfUsers(Users);
        await base.OnDisconnectedAsync(exception);
    }
}

whose SignalR Hub service is configured as :

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSignalR();
    ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    ...

    app.UseSignalR(routes =>
    {
        routes.MapHub<PopcornHub>("popcorn");
    });

    ...
}

In my client-side (WPF app), I have a service :

public class PopcornHubService : IPopcornHubService
{
    private readonly HubConnection _connection;

    public PopcornHubService()
    {
        _connection = new HubConnectionBuilder()
            .WithUrl($"{Utils.Constants.PopcornApi.Replace("/api", "/popcorn")}")
            .Build();
        _connection.On<int>("OnUserConnected", (message) =>
        {

        });
    }

    public async Task Start()
    {
        await _connection.StartAsync();
    }
}

My issue is that, when I call Start() method, I get the exception "Cannot start a connection that is not in the Initial state". The issue occurs either locally or in Azure.

The SignalR endpoint is fine but no connection can be established. What am I missing?

BrennanConroy commented 7 years ago

If your start method is called multiple times on the same PopcornHubService you will see that error.

Also, unrelated, but hubs aren't persistent so your Users value will be reset every hub method call.

moozzyk commented 7 years ago

This is by design at the moment. We may make the connection instance re-usable in the future.

RandyBuchholz commented 6 years ago

@moozzyk Is there a link to somewhere the discussion about reuse is taking place? The start and reuse thing is a learning stumbling block. From a use perspective, I would like to be able to declare a connection and just use it - hiding some of the plumbing. For example:

const connection = new signalR.HubConnection("/myHub").start();
const val1 = await connection.invoke("invocation1", pars);
const val2 = await connection.invoke("invocation2", pars);

Invoke could check to see if the connection was started and start it if not. Internally a state variable could be set if start was called previously, so it doesn't just start it unintentionally.

I was expecting to see connection/hub state exposed when I first encountered this issue.

if ( !connection.started ) { 
  connection.start().then( connection.Invoke("invocation1", pars) );
} else {
  connection.Invoke("invocation1", pars)
}

Even with the ability to check state, this seems to be a lot of unnecessary code for the user. I would never want to call invoke on a closed connection, so it seems that once I have started the hub at some previous point, invoke will always make sure the connection is open/opened.

Shhzdmrz commented 6 years ago

Is there any workaround to override this design? I am using it in React Native where I have a case to start and stop the connection and restart it. I am using alpha2 final because I have to use core 2.0 and ES5 version work only with React Native.

analogrelay commented 6 years ago

In the latest builds (preview 2) the connection is re-usable (start can be called after stop/close) and all builds are ES5. We don't support older previews so you'll have to update to preview 2 in order to get that functionality.

Shhzdmrz commented 6 years ago

so you mean it will work with ASP.NET Core 2.0?

analogrelay commented 6 years ago

No, SignalR is part of ASP.NET Core 2.1 and requires that. The alpha versions were prototype early releases designed to allow users to try the new APIs and give feedback, but since they released prior to any ASP.NET Core 2.1 release, they were built against ASP.NET Core 2.0.

From preview 2 on, SignalR has required ASP.NET Core 2.1 and it will require ASP.NET Core 2.1 when it RTMs

Shhzdmrz commented 6 years ago

so if I want to use SignalR I have to use ASP.NET Core 2.1 and only that will provide ES5 client.

analogrelay commented 6 years ago

The alpha has an ES5 client in dist/browser/signalr-clientES5-1.0.0-alpha2-final.js, but as I said, it is not supported and should not be used in production environments (there are major issues that have been fixed since then).

But yes, the final release of SignalR will require ASP.NET Core 2.1