flutter - websocket 自动重新连接 flutter 和 Riverpod?

标签 flutter dart websocket state-management riverpod

<强>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);
  }
}
  • 我创建了一个 connectProvider 提供程序,如下所示,我在 hostProvider 中“观察”以创建 channel 。每当出现异常时,我都会使用 Riverpod 库中的刷新函数来重新创建 channel :
  • // 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');
      });
    

    预先感谢您的帮助。

    最佳答案

    谢谢,@Dewey。

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

    我的提供商: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());
    
    var isSubControlError = false;  
    
    final sub = bStream.listen(
      (data) {
          ref
          .watch(channelProvider(httpParam))
          .sink
          ?.add('> sink add ${httpParam.path}');
       },
      onError: (_, stack) => null,
      onDone: () async {
          isSubControlError = true;
          await Future.delayed(Duration(seconds: 10));
          ref.container.refresh(channelProvider(httpParam));
      },
    );  
    
    ref.onDispose(() {
      log.i('streamProvider | Metrics - socket ${httpParam.path} closed');
      sub.cancel();
      if (isSubControlError == false)
        ref.watch(channelProvider(httpParam)).sink?.close(1001);
      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
      )
    

    关于flutter - websocket 自动重新连接 flutter 和 Riverpod?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64218088/

    相关文章:

    flutter - 如何从 QuerySnapshot 中获取特定文档的 DocumentSnapshot?

    firebase - Flutter 单元测试 Firestore

    Flutter GridView 不滚动

    java - 在 Java 中实现 WebSocket

    firebase - 我可以使用此快照调用访问文档ID吗?

    flutter - flutter 中的 SuperScript 和 SubScript

    dart - 在组件中绑定(bind)新类型,以便子组件可以注入(inject)它们

    flutter - List.map 返回 List<Future>

    go - 在 Go 中向特定客户端发送 Websocket 消息(使用 Gorilla)

    java - 如何使用 WebSocket 发送定期消息?