websocket自动与flutter和riverpod重新连接?

时间:2020-10-06 01:17:30

标签: flutter dart websocket state-management

1。目标

我希望在遇到网络问题或WebSocket服务器遇到问题时自动重建自定义WebSocket服务器(API)和Flutter应用之间的连接。

  • 用例1:wifi停止并突然恢复。
  • 用例2:API未启动,然后突然重新启动。
  • 约束:我将Riverpod用作状态管理库(并且我想保留它:)。 我强调状态管理库是因为我在StreamProvider中创建了WS连接(请参阅Riverpod)。

2。无需自动重新连接即可进行初始设置

  • 我创建了一个StreamProvider,如下所示:
final hostProvider =
  StreamProvider.autoDispose.family<Host, String>((ref, ip) async* {
  //SOCKET OPEN 
  final channel = IOWebSocketChannel.connect('ws://$ip:$port/v1/path');

  ref.onDispose(() {
    // SOCKET CLOSE
    return channel.sink.close();
  });

  await for (final json in channel.stream) {
    final jsonStr = jsonDecode(json as String);
    yield Host.fromJson(jsonStr as Map<String, dynamic>);
  }
});
  • 然后我创建了一个小部件来使用数据:
useProvider(hostProvider(ip)).when(
  data: (data) => show the result
  loading: () => show progress bar
  error: (error, _) => show error
);

这段代码很棒。但是,没有自动重新连接机制。

3。自动重新连接尝试

  1. 每当捕获到异常时,我都会在try / catch中调用函数connectWs:
final hostProvider =
    StreamProvider.autoDispose.family<Host, String>((ref, ip) async* {
  // Open the connection
  connectWs('ws://$ip:$port/v1/path').then((value) async* {
    final channel = IOWebSocketChannel(value);

    ref.onDispose(() {
      return channel.sink.close();
    });

    await for (final json in channel.stream) {
      final jsonStr = jsonDecode(json as String);
      yield Host.fromJson(jsonStr as Map<String, dynamic>);
    }
  });
});

Future<WebSocket> connectWs(String path) async {
  try {
    return await WebSocket.connect(path);
  } catch (e) {
    print("Error! " + e.toString());
    await Future.delayed(Duration(milliseconds: 2000));
    return await connectWs(path);
  }
}
  1. 我创建了一个connectProvider提供程序,如下所示,我在hostProvider中进行了“监视”以创建一个频道。每当有例外时,我都会使用Riverpod库中的刷新功能来重新创建频道:
// used in hostProvider
ref.container.refresh(connectProvider(ip))

final connectProvider =
  Provider.family<Host, String>((ref, ip) {
  //SOCKET OPEN 
  return IOWebSocketChannel.connect('ws://$ip:$port/v1/path');
  });

预先感谢您的帮助。

2 个答案:

答案 0 :(得分:0)

我对Riverpod有点初学者,但是在我看来,您想使用更高级别的redux / bloc样式流来在每次失败时重新创建提供程序...

当连接成功时,这个更高级别的bloc会创建提供程序;当连接失败时,您会向bloc调度一个事件,告诉它重新连接并重新创建提供程序...

这是我的想法,但是我还是这个软件包的初学者。

答案 1 :(得分:0)

谢谢@杜威。

最后,我找到了一种适用于我的用例的解决方法:

我的提供商:channelProvider和streamProvider

static final channelProvider = Provider.autoDispose
  .family<IOWebSocketChannel, HttpParam>((ref, httpParam) {
log.i('channelProvider | Metrics - $httpParam');
return IOWebSocketChannel.connect(
    'ws://${httpParam.ip}:$port/v1/${httpParam.path}');
}); 

static final streamProvider =
  StreamProvider.autoDispose.family<dynamic, HttpParam>((ref, httpParam) {
log.i('streamProvider | Metrics - $httpParam');
log.i('streamProvider | Metrics - socket ${httpParam.path} opened');    

var bStream = ref
    .watch(channelProvider(httpParam))
    .stream
    .asBroadcastStream(onCancel: (sub) => sub.cancel());    

final sub = bStream.listen(
  (data) => null,
  onError: (_, stack) => null,
  onDone: () async {
    await Future.delayed(Duration(seconds: 10));
    ref.container.refresh(channelProvider(httpParam));
  },
);  

ref.onDispose(() {
  log.i('streamProvider | Metrics - socket ${httpParam.path} closed');
  sub.cancel();
  bStream = null;
}); 

return bStream;
});

我以这种方式在小部件中使用streamProvider:

return useProvider(MetricsWsRepository.streamProvider(HttpParam(
  ip: ip,
  path: 'dummy-path',
))).when(
  data: (data) => deserialize & doSomething1,
  loading:() => doSomething2,
  error: (_, stack) => doSomething3
  )