Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot get client and/or server to work #10

Closed
davbaron opened this issue Apr 8, 2019 · 6 comments
Closed

Cannot get client and/or server to work #10

davbaron opened this issue Apr 8, 2019 · 6 comments

Comments

@davbaron
Copy link

davbaron commented Apr 8, 2019

I have a Flutter app that I would like to receive real-time broadcasts from my IIS .netcore web-api server. I initially had my web-api written in .net framework, but have now re-written it for .net core 2.2. As far as I can 'see' everything is setup properly, however there are two 'problems':

  1. On my server, my 'hub' class never seems to have any clients, i.e., the following statement never happens:
    Clients.All.SendCoreAsync("clientFunctionName", new object[] { name, message });
    The reason this never happens is that I have an If (Clients != null) test before it, and, Clients is always null. I have tried injecting IHubContext into my Hub class, but that has not helped. I am 'assuming' that Clients would in fact be null IF no clients had ever started() a signalR connection? Keep in mind that for my 'test' code I am doing an entire 'cycle' within the same Flutter class/screen. In other words, when I 'load' a Flutter screen, I open (start) the connection to my signalR hub. Then later on that same screen issues a web-api call to fetch some data. Within the .net core code then, before the web-api method 'returns' any data, but after the connection was started by the client, I make the hub method call to broadcast to all connected clients. But again, Clients is always null, so nothing is broadcast.

  2. As explained in a response to a different issue here, I intermittently receive the 'cannot await a future on a null _handshakeCompleter. Maybe the solution to that will 'fix' my first issue. Maybe even when I DO get a successful connection, I really am not connected? How else are the Clients always null, even after a successful connection/start?

I also have some general questions. I want to get things 'right' within my Flutter code. My screens (routes/classes) are 'basic vanilla Flutter - no MVC. Other than ScopedModel for some 'global' stuff, I use setState() and most presentation code exists in the same class as the event handling code. Currently I am 'declaring' the _hubConnection object var within my Flutter screen's (class's) declaration block. I then instantiate ( _hubConnection = HubConnectionBuilder().withUrl(_serverUrl).build() ) within the didChangeDependencies() method, which I have made async. Should I be doing it here? Versus ?? initState ?? I've tried it there and it did not seem to matter, but wanted to ask. Also, I am performing the instantiation within a setState((){}) block, so that _hubConnection is updated within overall state. Is that correct? Then again, the actual .start() call, which is an 'await' cannot be performed within a setState() block, so, ... is that ok?

I realize that this plug-in is not responsible for my server code. That said, I am hoping that you, or some other users doing similar things, might be able to shed some light on my situation. I will gladly provide more code if necessary.

Thank you!

@soernt
Copy link
Owner

soernt commented Apr 8, 2019

Regarding to 1.: This Libaray has an example of a very simple chat application within the Example folder. Do you have a minimal example which shows the issue?
Regarding to 2.: I never notice this happened for me. Can you or someone else having this issue create a repository with an example and tell me what I need to do in order to force this situation?

Regarding to your general Questions:
Creating a connection is expensive. I create a single connection for the whole application and inject it within my ViewModels. Creating a connection per Screen is not my recommended way of using a connection.

@davbaron
Copy link
Author

davbaron commented Apr 8, 2019

I will use the sample to see if it can act as a client to my own server.

For issue #2, I cannot 'make it happen', it just does sometimes, and does not other times. Another user suggested a possible reason for the error, as well as possible fix for it, but was not sure if there would be additional issues.

For the general question, I appreciate your advice. As far as opening (start) the hub connection, could you elaborate a bit further. The example (and the way I've done it) is that the connection is to a specific hub class on the server, i.e., 'chat'. But what if there are several functional areas within an app, i.e., chat, sales listings, forum postings, etc... Would you recommend having a single 'hub class' on the signalR server, but with multiple 'broadcast methods', one for each 'area of functionality', or would you recommend having multiple hub classes, one for each area of functionality, and then open multiple connections within the Flutter startup code?

I will place my hub connection code into my 'AppModel' (I'm using ScopedModel), which is inherited from essentially. But I will not do this until I can get this working on a test screen/class first.

@soernt
Copy link
Owner

soernt commented Apr 8, 2019

I will stress test my code to see If I can somehow get this error.
Did you use the logging options provided by the client to see some more details?

I would go with one hub class and add more function to it - because in this situation the client and Server has only one open connection that is established only once. Sure the other way is more clear/separated in therms of responsibility - but how many connection will stay open or gets open and closed during the usage or your app? The recommended Https connections are even more expensive.

@davbaron
Copy link
Author

davbaron commented Apr 8, 2019

I am not sure if 'stress' will cause the issue or not. On my system I am the only programmer, and there are no users, meaning, there is zero stress on the system. That is not to say that if you trie 1,000 times you will not hit it, but I do not think the failures are a matter of 'stress'.

In any case, no, I was not using logging. I set up logging as you show in the example and believe it or not I hit the error on the first 'try':

flutter: FINER: 2019-04-08 14:44:22.774604: Starting HubConnection.
flutter: FINER: 2019-04-08 14:44:22.776291: Starting connection with transfer format 'TransferFormat.Text'.
flutter: FINER: 2019-04-08 14:44:22.776585: Sending negotiation request: http://10.0.0.164:8890/Market/negotiate
flutter: FINEST: 2019-04-08 14:44:22.823715: HTTP send: url 'http://10.0.0.164:8890/Market/negotiate', method: 'POST' content: ''
flutter: FINER: 2019-04-08 14:44:22.933307: Selecting transport 'HttpTransportType.ServerSentEvents'
flutter: FINEST: 2019-04-08 14:44:22.933609: (SSE transport) Connecting
flutter: FINER: 2019-04-08 14:44:22.934263: Sending handshake request.
flutter: FINEST: 2019-04-08 14:44:22.934703: Sending message.
flutter: FINEST: 2019-04-08 14:44:22.934977: (SSE transport) sending data.
flutter: FINEST: 2019-04-08 14:44:22.946456: HTTP send: url 'http://10.0.0.164:8890/Market?id=ZjDfHGEA6J3hNfWa1-6G2A', method: 'POST' content: '{"protocol":"json","version":1}<…>
flutter: FINEST: 2019-04-08 14:44:23.011297: (SSE transport) data received
flutter: FINEST: 2019-04-08 14:44:23.011580: Incomming message
flutter: FINER: 2019-04-08 14:44:23.011820: Server handshake complete.
flutter: FINEST: 2019-04-08 14:44:23.013066: (SSE transport) request complete. Response status: 200.
flutter: INFO: 2019-04-08 14:44:23.013405: Using HubProtocol 'json'.
flutter: had error starting hub connection: NoSuchMethodError: The getter 'future' was called on null.
Receiver: null
Tried calling: future

closed screen… tried again:

flutter: FINER: 2019-04-08 14:46:55.195874: Starting HubConnection.
flutter: FINER: 2019-04-08 14:46:55.197036: Starting connection with transfer format 'TransferFormat.Text'.
flutter: FINER: 2019-04-08 14:46:55.197382: Sending negotiation request: http://10.0.0.164:8890/Market/negotiate
flutter: FINEST: 2019-04-08 14:46:55.217809: HTTP send: url 'http://10.0.0.164:8890/Market/negotiate', method: 'POST' content: ''
flutter: FINER: 2019-04-08 14:46:55.298880: Selecting transport 'HttpTransportType.ServerSentEvents'
flutter: FINEST: 2019-04-08 14:46:55.299313: (SSE transport) Connecting
flutter: FINER: 2019-04-08 14:46:55.300052: Sending handshake request.
flutter: FINEST: 2019-04-08 14:46:55.300503: Sending message.
flutter: FINEST: 2019-04-08 14:46:55.300856: (SSE transport) sending data.
flutter: FINEST: 2019-04-08 14:46:55.320439: HTTP send: url 'http://10.0.0.164:8890/Market?id=mpux6bg3aDhtblC_5jDGOA', method: 'POST' content: '{"protocol":"json","version":1}<…>
flutter: FINEST: 2019-04-08 14:46:55.422742: (SSE transport) request complete. Response status: 200.
flutter: INFO: 2019-04-08 14:46:55.423316: Using HubProtocol 'json'.
flutter: FINEST: 2019-04-08 14:46:55.424004: (SSE transport) data received
flutter: FINEST: 2019-04-08 14:46:55.424306: Incomming message
flutter: FINER: 2019-04-08 14:46:55.424648: Server handshake complete.

followed a few seconds later by a never ending (until screen is closed) series of:

flutter: FINEST: 2019-04-08 14:59:37.381472: Incomming message
flutter: FINEST: 2019-04-08 14:59:37.381784: Handle message of type 'MessageType.Ping'.
flutter: FINEST: 2019-04-08 14:59:43.431655: Sending message.
flutter: FINEST: 2019-04-08 14:59:43.432251: (SSE transport) sending data.
flutter: FINEST: 2019-04-08 14:59:43.432916: HTTP send: url 'http://10.0.0.164:8890/Market?id= mpux6bg3aDhtblC_5jDGOA', method: 'POST' content: '{"type":6}<…>
flutter: FINEST: 2019-04-08 14:59:43.473388: (SSE transport) request complete. Response status: 200.
flutter: FINEST: 2019-04-08 14:59:53.365052: (SSE transport) data received

At no point does my 'broadcast handler' get called. This would be due to the Clients object on the server always being NULL. I can certainly see in the above logging that a 'connection' seems to be getting made, but for some reason the server never populates the Clients object. I am not sure how that works and such.

@davbaron
Copy link
Author

davbaron commented Apr 8, 2019

Another note: I added this method to my Hub: public override async Task OnConnectedAsync()

After calling .start() from the client, this method DOES get triggered. Within that method I am able to access the Context.ConnectionId value, and sure enough it matches the 'id' value you get back in your connection code (on the client). That said, the Hub.Clients collection is always NULL. After doing some research on that, the apparent consensus was that I had to inject IHubContext into my hub. So I did the following in my hub class:

protected IHubContext<MyHub> _context;

public MyHub(IHubContext<MyHub> context)
{
   _context = context;
}

Then, when my 'Controller' class is instantiated, I do the following:

public class MyController : ControllerBase
{
    private readonly IHubContext<MyHub> _myHubContext;

    public MyController(IHubContext<MyHub> hubContext)
    {
        _myHubContext = hubContext;
    }

Then, within a web-api method within the controller, I do the following:

MyHub srHub = new MyHub(_myHubContext);
srHub.testmethod("fred", "test");

The 'testmethod' code in turn does the test for Clients != null... but it's always null, so nothing is broadcast. I will keep researching on how to get Clients populated...but any thoughts you (or anyone else) have would be great.

@davbaron
Copy link
Author

davbaron commented Apr 9, 2019

SUCCESS! I finally got things working. As can be seen in my prior comment, I was declaring a protected member of the hub class: protected IHubContext _context;

The problem was that the _context was not persistent, so when the controller class instantiated the hub (so send a broadcast), the _context was 'gone' and thus no 'Clients'. The solution was declaring the member as static: private static IHubContext _context;

Now when the controller method instantiates the hub, it uses the static _context object which of course maintains all connected clients.

So, as long as the client can 'start' a successful connection, I am good to go, including receiving complex data objects in the payload (all my models have .fromJson methods).

I am now going to change my Flutter code to open the connection within the AppModel and then within the various inherited screens I will try to 'handle' their own special broadcast messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants