5

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", "");
    }
Paul
  • 9,409
  • 13
  • 64
  • 113
  • 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 wrong – Camilo Terevinto May 24 '18 at 11:45
  • I've updated it to clarify that the issue happens regardless of whether it has an await or a .Wait() – Paul May 24 '18 at 11:54
  • 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? – Camilo Terevinto May 24 '18 at 11:58
  • 1
    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 [mcve] – Nkosi May 24 '18 at 12:01
  • 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 xaml – Paul May 24 '18 at 12:02
  • @Nkosi It's passing in the host + "/hubname". – Paul May 24 '18 at 12:04
  • 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. – Nkosi May 24 '18 at 12:06
  • Updated the question. It's just instantiated from the constructor of a WPF window. – Paul May 24 '18 at 12:09
  • It could be a compatibility problem between SignalR 2 and SignalR Core. Do you see any useful output on the SignalR 2 server? – Camilo Terevinto May 24 '18 at 12:10
  • 1
    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. – Tomas Chabada May 24 '18 at 12:14
  • Try to call `Task.Run(() => StartIfNeededAsync());` in constructor instead of `StartIfNeededAsync();` – Tomas Chabada May 24 '18 at 12:21
  • 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 successfully – Paul May 24 '18 at 12:28
  • I wrote a basic console app to rule out WPF involvement but no change – Paul May 24 '18 at 12:32
  • When task is ended successfully, it means that connection was made or an exception occurred. Can you confirm this? – Tomas Chabada May 24 '18 at 12:36
  • On the StartAsync, the task is RanToCompletion, Void result and a null exception (which sounds like it's worked to me...) – Paul May 24 '18 at 12:40

2 Answers2

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", "");
Paul
  • 9,409
  • 13
  • 64
  • 113
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • I can't see how moving one call from a constructor to the same place in the calling method is going to help here? – Paul May 24 '18 at 12:50
  • @Paul It could be a deadlock issue, and the explicit `await` on a separate call could fix that. That's the difference – Camilo Terevinto May 24 '18 at 12:51
  • @Paul By not awaiting the async call you could be calling Invoke before the connection has time to complete. – Nkosi May 24 '18 at 12:52
  • @Paul reviewing a sample on GitHub, the `StartAsync` is a distinct step that must be awaited. https://github.com/aspnet/SignalR/blob/4ac8e786cfd86fa3b40321f932ea0db00f9d8ae1/samples/ClientSample/HubSample.cs – Nkosi May 24 '18 at 13:05
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://stackoverflow.com/a/58551924/603807

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.

dyslexicanaboko
  • 4,215
  • 2
  • 37
  • 43
  • 1
    This doesn't actually help, when the `.StartAsync()`-call doesn't ever return. – Squirrelkiller Apr 22 '20 at 17:17
  • 1
    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. – dyslexicanaboko Apr 30 '20 at 01:36
  • 1
    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. – Squirrelkiller Apr 30 '20 at 06:51