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

The websocket server is closed, and the client does not perceive the link closing #281

Open
leeyisoft opened this issue Aug 8, 2023 · 7 comments

Comments

@leeyisoft
Copy link

leeyisoft commented Aug 8, 2023

The websocket server is closed, and the client does not perceive the link closing:

I run the macOS(desktop) version of Flutter on my MacBook and close the lid of my laptop after work.

The next morning I came back and found that isConnected was still true, that is, the websocket link status of the macOS(desktop) version of Flutter was still connected, but it was no longer sending ping to the server.

The link state of the server has long been closed, if without restarting the APP, is there any way for the program to automatically check this state and self-repair the link state as soon as possible


  bool get isConnected =>
      _webSocketChannel != null && _webSocketChannel?.sink != null;


...
  /// 开启WebSocket连接
  Future<void> openSocket() async {
    var connectivityResult = await (Connectivity().checkConnectivity());
    if (connectivityResult == ConnectivityResult.none) {
      debugPrint('> ws openSocket 网络连接异常ws');
      return;
    }
    if (UserRepoLocal.to.isLogin == false) {
      debugPrint('> ws openSocket is not login');
      return;
    }

    // 链接状态正常,不需要任何处理
    if (isConnected) {
      debugPrint('> ws openSocket _socketStatus: $_socketStatus;');
      return;
    }


    try {
      Map<String, dynamic> headers = await defaultHeaders();
      headers[Keys.tokenKey] = UserRepoLocal.to.accessToken;

      _webSocketChannel = IOWebSocketChannel.connect(
        WS_URL,
        headers: headers,
        pingInterval: Duration(milliseconds: _heartTimes),
        protocols: protocols,
      );
...

full code https://github.com/imboy-pub/imboy-flutter/blob/main/lib/service/websocket.dart

] flutter: > ws openSocket _socketStatus: SocketStatus.SocketStatusConnected;

      //底部导航条
      bottomNavigationBar: Obx(
        () => BottomNavigationBar(
          // 当前菜单下标
          currentIndex: state.bottomBarIndex.value,
          // 点击事件,获取当前点击的标签下标
          onTap: (int index) {
            logic.changeBottomBarIndex(index);
            // 控制 PageView 跳转到指定的页面
            pageController.jumpToPage(index);
          },




  //改变底部导航栏索引
  void changeBottomBarIndex(int index) {
    // 检查WS链接状态
    WebSocketService.to.openSocket();
    state.bottomBarIndex.value = index;
    iPrint("changeBottomBarIndex index $index");
  }
@jeffscaturro-aka
Copy link

jeffscaturro-aka commented Aug 22, 2023

We're also experiencing something similar, and any help or guidance would be sincerely appreciated. We are also utilizing IOWebSocketChannel, though we see this behavior with "out of the box" WebSocket implementation as well.

  • Client sends upgrade request.
  • Server receives upgrade request.
  • Server transforms upgrade request.
  • Server & client trigger ready.
  • Client sends message.
  • Server receives message.
  • Server sends message back.
  • Server closes connection 1001 goingAway.
  • Client never receives message or closure.

I was thinking the message the server is sending is causing the stream to pause or close, but other times it works just fine. This is happening in production behind AWS infrastructure.

pingIntervals are set to 5 seconds on both client and server's connection. It appears the ping fails sometimes after the server sends the message back.

No errors are being raised either. Any ideas? It'd be much appreciated!

@jeffscaturro-aka
Copy link

@leeyisoft one suggestion I've seen on some other threads is to implement a custom ping/pong so that you can reconnect if it fails. Though I'd like to still get to the bottom of what's causing, or what could cause, what we're seeing.

@leeyisoft
Copy link
Author

leeyisoft commented Aug 23, 2023

@leeyisoft我在其他一些线程上看到的一个建议是实现自定义 ping/pong,以便在失败时可以重新连接。尽管我仍然想弄清楚到底是什么原因造成或可能导致我们所看到的情况。

@jeffscaturro-aka
我认为“实现自定义 ping/pong”和“web_socket_channel” 里面默认的ping/pang 是一样的,没法解决问题

我这里的情况是:
1 我在MacBook上面开发,晚上的时候我就把电脑盖上了;
2 过一段时间就断网了,但是APP运用着,不知道发送ping消息的定时器因为什么原因死掉了;
3 服务端长时间没有收到ping 就主动关闭了TCP链接,服务器应该是给客户端发送了关闭链接的消息的,但是因为客户端网络一直链接不上,我猜测服务端应该是完成了一系列的异常处理后还是任务客户端已经下线了
4 第二天早上再打开MacBook的时候,客户端的APP进程还在操作系统中运行,客户端发送ping消息的Timer已经死了(不知道为什么死了,我认为这是问题所在)

如果能够知道“客户端发送ping消息的Timer死的原因”,应该可以解决问题

我现在的解决方案是:

1 在 WebSocket单例中添加一个 lastConnectedAt 属性;
2 在WebSocket链接成功的时候记录当前时间;
3 在每次主动检查WebSocket链接状态的逻辑中 判断 (now - lastConnectedAt) > 3600000 为true的时候就主动关闭 链接后重新连接

这样不完美,但是可以缓解问题

···
/// 开启WebSocket连接
Future openSocket() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
debugPrint('> ws openSocket 网络连接异常ws');
return;
}
if (UserRepoLocal.to.isLogin == false) {
debugPrint('> ws openSocket is not login');
return;
}
int now = DateTimeHelper.currentTimeMillis();
if ((now - lastConnectedAt) > 3600000) {
closeSocket(false);
}
// 链接状态正常,不需要任何处理
if (isConnected) {
debugPrint('> ws openSocket _socketStatus: $_socketStatus;');
return;
}
···
这是我的代码, https://github.com/imboy-pub/imboy-flutter/blob/dev/lib/service/websocket.dart

项目是全开源的,但是要运行清理,需要自己配置服务端(做了很多安全校验相关的功能,APP端没有对应的服务端的密钥,没法直接运行)

@leeyisoft
Copy link
Author

The client's pingIntervals need to be smaller than the server's idle_timeout, taking into account the network round-trip time

客户端的 pingIntervals 需要比 服务端 idle_timeout 小一些,网络往返时间考虑进去

@jeffscaturro-aka
Copy link

@leeyisoft Thank you for the additional information! While your strategy does seem to be a valid approach in having the client reconnect, it does seem to be our situations are slightly different. I'm experiencing the websocket close very shortly after the connection is opened. Just a couple seconds. I also have tried with the client's ping interval shorter than the server's, even accounting for the round-trip time.

@leeyisoft
Copy link
Author

@leeyisoft Thank you for the additional information! While your strategy does seem to be a valid approach in having the client reconnect, it does seem to be our situations are slightly different. I'm experiencing the websocket close very shortly after the connection is opened. Just a couple seconds. I also have tried with the client's ping interval shorter than the server's, even accounting for the round-trip time.

You can find the problem according to the WebSocket connection close code and do not know whether it is useful for you

https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code

@shanelau
Copy link

Same error.

The client does not know that the connection has been disconnected and will not try to reconnect again.

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

3 participants