SignalR 2 StartAsync never returns
Asked Answered
T

2

6

In the following part of a class, StartAsync never returns.

Any ideas why? The server appears to be working fine, and works with Javascript clients.

SignalR client version is v1.0.0-rc1-final

    public HubUtil(string baseUrl) //string clientId
    {
        connection = new HubConnectionBuilder()
            .AddJsonProtocol()
            .WithUrl(baseUrl)  // baseUrl is "https://hostname/hubname"
            .Build();

        connection.Closed += Connection_Closed;
        StartIfNeededAsync();
    }

    private Task Connection_Closed(Exception arg)
    {
        return StartIfNeededAsync();
    }

    public async Task StartIfNeededAsync()
    {
        if (_connectionState == ConnectionState.Connected)
        {
            return;
        }

        try
        {
            await connection.StartAsync(); // Never connects
            _connectionState = ConnectionState.Connected;
        }
        catch (Exception ex)
        {
            _connectionState = ConnectionState.Faulted;
            throw;
        }
    }

From a basic console app this is how hubutil is called:

    static void Main(string[] args)
    {
        var hub = new HubUtil("https://host/hubname");
        hub.Invoke("checkin", "id", "");
    }
Tekla answered 24/5, 2018 at 11:41 Comment(15)
It will be much better for you if you read the warnings that Visual Studio is throwing at you. StartIfNeededAsync is an async method without await, that's the first sign something is terribly wrongMelanism
I've updated it to clarify that the issue happens regardless of whether it has an await or a .Wait()Tekla
If you look at the output window, does anything else happens? Does it timeout if you leave it for a minute, for example? From where are you instantiating HubUtil? What project type is this running on?Melanism
Are you passing a valid URL? Your comments just say clientId. If not calling a proper URL it will hang as there is nothing to connect to. Show a minimal reproducible exampleNomanomad
Quite literally nothing happens...no output in the console. It's a .net standard 2.0 library, running from a WPF application. It's instantiated from a main window xamlTekla
@Nomanomad It's passing in the host + "/hubname".Tekla
Then the issue may be related to other factors external to the code shown. Show how and where this util is called. blocking on StartAsync is the symptom and not necessarily the cause.Nomanomad
Updated the question. It's just instantiated from the constructor of a WPF window.Tekla
It could be a compatibility problem between SignalR 2 and SignalR Core. Do you see any useful output on the SignalR 2 server?Melanism
Some months before, I ran into similar problem but with different third party library. Then, problem was hidden inside asynchronous methods. They were synchronously waiting on asynchronous code behind the scene. Only workaround was to wrap my call to their async code to Task.Run. Then I made it running on threadpool thread and suddenly it worked.Priory
Try to call Task.Run(() => StartIfNeededAsync()); in constructor instead of StartIfNeededAsync();Priory
Well it doesn't block anything for sure, but SignalR isn't connecting still. Although now the task is ending, it says it's ended successfullyTekla
I wrote a basic console app to rule out WPF involvement but no changeTekla
When task is ended successfully, it means that connection was made or an exception occurred. Can you confirm this?Priory
On the StartAsync, the task is RanToCompletion, Void result and a null exception (which sounds like it's worked to me...)Tekla
N
0

Probably trying to do too many things at the same time.

Remove StartIfNeededAsync from constructor

public HubUtil(string baseUrl) {
    connection = new HubConnectionBuilder()
        .AddJsonProtocol()
        .WithUrl(baseUrl)  // baseUrl is "https://hostname/hubname"
        .Build();

    connection.Closed += Connection_Closed;       
}

private Task Connection_Closed(Exception arg) {
    return StartIfNeededAsync();
}

public async Task StartIfNeededAsync() {
    if (_connectionState == ConnectionState.Connected) {
        return;
    }

    try {
        await connection.StartAsync();
        _connectionState = ConnectionState.Connected;
    } catch (Exception ex) {
        _connectionState = ConnectionState.Faulted;
        throw;
    }
}

//...

and make it an explicit call also taking SSL into consideration.

//Handle TLS protocols
System.Net.ServicePointManager.SecurityProtocol =
    System.Net.SecurityProtocolType.Tls
    | System.Net.SecurityProtocolType.Tls11
    | System.Net.SecurityProtocolType.Tls12;

var hub = new HubUtil("https://host/hubname");
await hub.StartIfNeededAsync();
hub.Invoke("checkin", "id", "");
Nomanomad answered 24/5, 2018 at 11:43 Comment(4)
I can't see how moving one call from a constructor to the same place in the calling method is going to help here?Tekla
@Tekla It could be a deadlock issue, and the explicit await on a separate call could fix that. That's the differenceMelanism
@Tekla By not awaiting the async call you could be calling Invoke before the connection has time to complete.Nomanomad
@Tekla reviewing a sample on GitHub, the StartAsync is a distinct step that must be awaited. github.com/aspnet/SignalR/blob/…Nomanomad
F
0

I answered a similar question on SO already. I don't want to copy and paste it here so here is the link instead:

https://mcmap.net/q/194104/-signalr-console-app-example

Since this is a link to my own answer on the SO site I would hope this takes a pass for the moderators who don't like link only answers.

Foster answered 25/10, 2019 at 3:50 Comment(3)
This doesn't actually help, when the .StartAsync()-call doesn't ever return.Expedition
I think it "appears" as though it never returns because I was having the same problem. You have to wait for it to return by actually using the wait method. Yes I know that defeats the purpose of Async calls, but it's not my API. I have researched this a lot and I cannot find another solution.Foster
I meant on the http-Level the call never returns - so it won't make a difference whether or not I .Wait() for it. Anyway, I found the solution to my specific problem: Had ot install the WebSockets-feature for IIS.Expedition

© 2022 - 2024 — McMap. All rights reserved.