Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/webpack/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Flutter websocket自动重新连接到颤振&;河豚?_Flutter_Dart_Websocket_State Management_Riverpod - Fatal编程技术网

Flutter websocket自动重新连接到颤振&;河豚?

Flutter websocket自动重新连接到颤振&;河豚?,flutter,dart,websocket,state-management,riverpod,Flutter,Dart,Websocket,State Management,Riverpod,1。目标 我希望在遇到网络问题或WebSocket服务器遇到问题时,能够自动重新建立我的自定义WebSocket服务器(API)和我的Flatter应用程序之间的连接 用例1:wifi停止并突然恢复 用例2:API没有启动,而是突然重新启动 约束:我使用Riverpod作为状态管理库(我想保留它:)。 我之所以强调状态管理库,是因为我在StreamProvider中创建了WS连接(参见Riverpod) 2。没有自动重新连接的初始设置 我创建了一个StreamProvider,如下所示:

1。目标

我希望在遇到网络问题或WebSocket服务器遇到问题时,能够自动重新建立我的自定义WebSocket服务器(API)和我的Flatter应用程序之间的连接

  • 用例1:wifi停止并突然恢复
  • 用例2:API没有启动,而是突然重新启动
  • 约束:我使用Riverpod作为状态管理库(我想保留它:)。 我之所以强调状态管理库,是因为我在StreamProvider中创建了WS连接(参见Riverpod)
2。没有自动重新连接的初始设置

  • 我创建了一个StreamProvider,如下所示:
这段代码非常有效。但是,没有自动重新连接机制

3。自动重新连接尝试

  • 每当捕获异常时,我都在try/catch中调用函数connectWs:
  • 最终主机提供程序=
    StreamProvider.autoDispose.family((ref,ip)异步*{
    //打开连接
    connectWs('ws://$ip:$port/v1/path')。然后((值)异步*{
    最终通道=IOWebSocketChannel(值);
    参考onDispose(){
    返回channel.sink.close();
    });
    等待(channel.stream中的最终json){
    最终jsonStr=jsonDecode(json作为字符串);
    产生Host.fromJson(jsonStr作为映射);
    }
    });
    });
    未来的connectWs(字符串路径)异步{
    试一试{
    返回等待WebSocket.connect(路径);
    }捕获(e){
    打印(“错误!”+e.toString());
    等待未来。延迟(持续时间(毫秒:2000));
    返回等待连接(路径);
    }
    }
    
  • 我创建了一个connectProvider提供程序,如下所示,我在hostProvider中“监视”以创建通道。每当出现异常时,我使用Riverpod库中的刷新功能重新创建通道:
  • //在hostProvider中使用
    参考容器刷新(连接提供程序(ip))
    最终连接提供程序=
    提供者。家庭((参考,ip){
    //插座打开
    返回IOWebSocketChannel.connect('ws://$ip:$port/v1/path');
    });
    

    提前感谢您的帮助。

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

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

    这是我的想法,但再说一次,我是这个软件包的初学者。

    谢谢,@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;
    });
    
    useProvider(hostProvider(ip)).when(
      data: (data) => show the result
      loading: () => show progress bar
      error: (error, _) => show error
    );
    
    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);
      }
    }
    
    // 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');
      });
    
    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;
    });
    
    return useProvider(MetricsWsRepository.streamProvider(HttpParam(
      ip: ip,
      path: 'dummy-path',
    ))).when(
      data: (data) => deserialize & doSomething1,
      loading:() => doSomething2,
      error: (_, stack) => doSomething3
      )