1

I have a controller (MyController), which requests data from a websocket connection (wsService) while initialising.

What is the best way to detect and wait that the websocket connection is open and then place the request from the controller?

Now I'm using following solution:

my_controller.dart:

MyController(wsService ws){
  // when refresh() in wsService is called, 
  // the call is redirected to MyController's load()
  ws.refresh = load; 
}

load(){
  ws.send(request);
}

ws_service.dart:

onConnect(){ //this is called when websocket connection is opened
  refresh(); //this calls MyController's load()
}
grohjy
  • 2,059
  • 1
  • 18
  • 19
  • http://stackoverflow.com/questions/22417566 contains a nice example (see web\main.dart). You need to use async programming with Futures. `webSocket.onOpen.onOpen.first.then((x) => onConnect());` – Günter Zöchbauer Mar 16 '14 at 11:05
  • I just saw this example is from you. I don't understand your question then. – Günter Zöchbauer Mar 16 '14 at 11:06
  • My problem is that, when MyController initializes, the websocket connection is not yet open. In my question I have given a solution for that problem (the load() is called after the connection is open), but I'm not sure is it the best way to do it. – grohjy Mar 16 '14 at 11:12
  • I haven't tried it but you could check ws.readyState and when it is not yet open you can listen for 'open' like in your example. https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants – Günter Zöchbauer Mar 16 '14 at 11:17
  • @GünterZöchbauer thank you. I removed `ws.refresh = load;` and updated MyController's constructor: `_scope.$watch(() => ws.webSocket.readyState, (state) => (state == WebSocket.OPEN) ? load() : null);` Now it's much easier to understand what is happening and also the wsService is not polluted anymore. I think you should "answer" with this solution. – grohjy Mar 16 '14 at 12:17

3 Answers3

2

I still think you should do something like this instead of make Angular polling the state.

MyController(wsService ws){
  if(ws.readyState == WebSocket.OPEN) {
    load();
  } else {
    ws.onOpen.first.then((_) => load());
  }
}

load(){
  ws.send(request);
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • You are right, the polling is not efficient. But I rather handle websocket connection (opening, closing etc.) in one place, therefore I would use `Timer.periodic` to test the state and when the state is "open", call `load()` and cancel the timer. I tried to include the example code here, but couldn't. – grohjy Mar 16 '14 at 14:07
  • But that is also polling. What do you think doesn't work with my example? – Günter Zöchbauer Mar 16 '14 at 14:10
  • You can create your own answer and post the code there to make it available for discussions. – Günter Zöchbauer Mar 16 '14 at 14:11
  • if the `ws.onOpen.first.then((_) => load());` from the controller runs first, then the callback function in wsService won't register. In other words: if the connection is open, then `ws.onOpen.first.then((_) => ...);` won't do anything. – grohjy Mar 16 '14 at 14:24
  • Sorry had a bug, I removed the `!` after the `if(`. I think it should work this way. – Günter Zöchbauer Mar 16 '14 at 14:27
  • Yes I noticed the bug. Also there should be one `)` after `load()`, but these were not causing the problem I described earlier. – grohjy Mar 16 '14 at 14:33
  • If the connection is open `ws.onOpen.first.then((_) => load());` shouldn't do anything, than just `load();` should be executed, this is what your example does. If it's not yet open it should call `load();` when it actually got opened. This is what `onOpen.first.then((_) => ...)` is for. – Günter Zöchbauer Mar 16 '14 at 14:41
  • If the `onOpen...` runs, then the same function won't run in `wsService` and that is the problem, because there are all other relevant functions that must be executed after the connection is opened. – grohjy Mar 16 '14 at 15:08
0

This solution is still using polling, but with this solution the handling of websocket connection is kept in one place (wsService) and there is no duplicates in function calls.

MyController(wsService ws){
  new Timer.periodic(new Duration(milliseconds: 100), (t){
    if(ws.webSocket.readyState == WebSocket.OPEN) {
      t.cancel();
      load();
    }
  });
}

load(){
  ws.send(request);
}
grohjy
  • 2,059
  • 1
  • 18
  • 19
0

Using this package https://pub.dev/packages/websocket_universal you can just await connection like this:

await textSocketHandler.connect();

Full example:

// ignore_for_file: avoid_print
import 'package:websocket_universal/websocket_universal.dart';

/// Example works with Postman Echo server
void main() async {
  /// 1. Create webSocket handler:
  final textSocketHandler = IWebSocketHandler<String, String>.createClient(
    'wss://ws.postman-echo.com/raw', // Postman echo ws server
    SocketSimpleTextProcessor(),
  );

  /// 2. Listen to webSocket messages:
  textSocketHandler.incomingMessagesStream.listen((inMsg) {
    print('> webSocket  got text message from server: "$inMsg" '
        '[ping: ${textSocketHandler.pingDelayMs}]');
  });
  textSocketHandler.outgoingMessagesStream.listen((inMsg) {
    print('> webSocket sent text message to   server: "$inMsg" '
        '[ping: ${textSocketHandler.pingDelayMs}]');
  });

  /// 3. Connect & send message:
  await textSocketHandler.connect();
  textSocketHandler.sendMessage('Hello server!');
  await Future<void>.delayed(const Duration(seconds: 4));

  // 4. Disconnect & close connection:
  await textSocketHandler.disconnect('manual disconnect');
  textSocketHandler.close();
}
Dmitrii Matunin
  • 275
  • 4
  • 5