Netty websocket客户端闲置5分钟后不从服务器读取新帧

2024-03-26

我在服务器端和客户端都使用 Netty 来建立和控制 websocket 连接。我在服务器端有一个空闲状态处理程序 http://netty.io/4.1/api/io/netty/handler/timeout/IdleStateHandler.html当通道读取器、写入器或两者闲置一段时间后,它将发送用户事件。我设置了写入器空闲事件将在空闲 5 分钟后触发,读取器空闲事件将在空闲 6 分钟后触发。在写入器空闲事件期间,服务器将向客户端发送 ping 帧,一旦从客户端接收到 pong 帧,这将重置写入器空闲时间以及读取器空闲时间。

问题是,netty 客户端在闲置 5 分钟后似乎没有读取任何新帧。我对客户端中的通道进行了一些状态检查,以查看该通道在 5 分钟空闲期后是否可写、已注册、打开和活动,并且所有状态均为 true,但未读取新帧。为了解决这个问题,我只是将服务器端的 IdleStateHandler 时间更改为 3 分钟而不是 5 分钟,以便客户端在空闲 ​​5 分钟之前收到 ping 帧并响应 pong 帧。

但这并不能解决根本问题。我希望了解并能够控制客户端的读取器何时闲置,并能够防止未来出现丢失或未读数据的问题。看下面的代码,如果没有从客户端收到 pong 或 heartbeat 帧,空闲事件处理程序将关闭通道连接,但由于客户端不读取新帧,因此它永远不会获取关闭帧,因此服务器认为客户端未连接,而客户端认为已连接,这显然会导致问题。有没有什么方法可以使用 Netty 在客户端更好地控制这个神奇的 5 分钟超时?我在文档或来源中找不到任何有关此内容的信息。

下面是服务器中相关的空闲事件处理代码:

private class ConnectServerInitializer extends ChannelInitializer<SocketChannel> {

    private final IdleEventHandler idleEventHandler = new IdleEventHandler();
    private final SslContext sslCtx;

    private ConnectServerInitializer(SslContext sslCtx) {
        this.sslCtx = sslCtx;
    }

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (sslCtx != null) {
            pipeline.addLast(sslCtx.newHandler(ch.alloc()));
        }
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new HttpObjectAggregator(65536));
        pipeline.addLast(idleEventHandler.newStateHandler());
        pipeline.addLast(idleEventHandler);
        pipeline.addLast(getHandler());
    }

}

@Sharable
private class IdleEventHandler extends ChannelDuplexHandler {

    private static final String HEARTBEAT_CONTENT = "--heartbeat--";
    private static final int READER_IDLE_TIMEOUT = 200; // 20 seconds more that writer to allow for pong response
    private static final int WRITER_IDLE_TIMEOUT = 180; // NOTE: netty clients will not read frames after 5 minutes of being idle
    // This is a fallback for when clients do not support ping/pong frames
    private final AttributeKey<Boolean> USE_HEARTBEAT = AttributeKey.valueOf("use-heartbeat");

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object event) throws Exception {
        if (event instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) event;
            Boolean useHeartbeat = ctx.attr(USE_HEARTBEAT).get();
            if (e.state() == IdleState.READER_IDLE) {
                if (useHeartbeat == null) {
                    logger.info("Client " + ctx.channel() + " has not responded to ping frame. Sending heartbeat message...");
                    ctx.attr(USE_HEARTBEAT).set(true);
                    sendHeartbeat(ctx);
                } else {
                    logger.warn("Client " + ctx.channel() + " has been idle for too long. Closing websocket connection...");
                    ctx.close();
                }
            } else if (e.state() == IdleState.WRITER_IDLE || e.state() == IdleState.ALL_IDLE) {
                if (useHeartbeat == null || !useHeartbeat) {
                    ByteBuf ping = Unpooled.wrappedBuffer(HEARTBEAT_CONTENT.getBytes());
                    ctx.writeAndFlush(new PingWebSocketFrame(ping));
                } else {
                    sendHeartbeat(ctx);
                }
            }
        }
    }

    private void sendHeartbeat(ChannelHandlerContext ctx) {
        String json = getHandler().getMessenger().serialize(new HeartbeatMessage(HEARTBEAT_CONTENT));
        ctx.writeAndFlush(new TextWebSocketFrame(json));
    }

    private IdleStateHandler newStateHandler() {
        return new IdleStateHandler(READER_IDLE_TIMEOUT, WRITER_IDLE_TIMEOUT, WRITER_IDLE_TIMEOUT);
    }
}

您的问题与防火墙超时有关。某些防火墙的超时时间接近 5 分钟,如果超过此超时,连接将被静默断开。因此,客户端和服务器需要有一些读取超时来检查这一事实,并且服务器、客户端或两者都有某种 ping 消息。当您在 IPv6 上运行协议时,防火墙问题会减少,因为大多数 IPv6 防火墙主要是无状态的,通常不会更改连接的端口,因此来自客户端的数据包会再次重新激活防火墙中的条目。

当您多次出现 5 分钟超时时,您应该考虑是否可以将来自 Websocket 的额外负载与每 1 分钟一次简单轮询 http 循环的负载进行比较,因为这会减少服务器上的内存压力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Netty websocket客户端闲置5分钟后不从服务器读取新帧 的相关文章

随机推荐

  • 多核ARM cpu上的中断如何工作

    这个问题已经针对 x86 得到了解答 但是 我找不到太多关于 ARM MP cpu 如 Cortex A9 Cortex A15 等 的信息 更重要的是我想知道是否可以在非主CPU上引发中断而无需任何配置等 我正在开发一款仅处理主 cpu
  • 确定 gcry_cipher_decrypt 解密数据的大小?

    我使用的是 AES GCM 但以下是其他模式 如 AES CBC 的一般问题 我有以下电话libgcrypt define COUNTOF x sizeof x sizeof x 0 define ROUNDUP x b x x b 1 b
  • Android - 创建自定义 SearchView

    我有一个列表活动 其中包括 SearchView 和 ListView 我希望 SearchView 类似于谷歌地图 Android 应用程序中的内容 目前我在activity main xml中的SearchView代码是
  • 具有 +1 逻辑的 4 位计数器 D 触发器

    我正在尝试通过 Verilog 实现带有 1 逻辑的 D 触发器计数器 但是我收到了很多有关网络多个常量驱动程序的错误代码 有人可以帮我吗 这是到目前为止的代码 module LAB clk clear Enable Q input clk
  • 将 margin-bottom 添加到页脚之前的图像

    我想看看是否有办法添加margin bottom to an img仅当元素后跟一个footer元素 目前我正在使用一个特殊的类 image before footer 在我最后一次img元素来实现这一点 但想知道是否有办法在 CSS 中动
  • 有没有人能够成功使用 albahari.com 的 PredicateBuilder 来对抗 MongoDB?

    我使用 albahari com 中流行的 PredicateBuilder 编写了以下代码 var predicate PredicateBuilder False
  • 增加tomcat 7中的并发连接数

    想知道如何增加tomcat 7中的并发连接数 我做了一些研究 从apache文档中发现 设置acceptCount maxConnections的值并增加maxThreads可以做到这一点 但是当尝试时 我只能提交500个请求1000 下面
  • 多字段匹配查询

    我对弹性搜索相当陌生 想编写一个涉及两个字段的查询 我的意思是字段的内容包含指定的子字符串 我有一个包含字段的文档 如下所示 name n tag t 我试过这个 search d query match name n tag t 但查询结
  • androidx.legacy:legacy-support-v4: 依赖有什么用

    我正在实现 Android Room 数据库 在其中一篇教程中我发现了使用androidx legacy legacy support v4 1 0 0依赖性 谁能告诉我这种依赖关系的用途 androidx legacy legacy su
  • Linux 上的 PHP 和 RAW Sockets

    做一个就足够了 sudo setcap cap net raw eip usr bin php5 能够在 Linux 中的 PHP 而不是 CLI 中使用 RAW SOCKETS 吗 如果是 那么它不起作用 但开始在 CLI 中工作但不使用
  • 如何在 C# 中解析 HTTP url?

    我的要求是解析 Http URL 并相应地调用函数 在我当前的实现中 我使用嵌套的 if else 语句 我认为这不是一种优化的方式 您能建议其他一些有效的方法吗 网址是这样的 server func1 server func1 SubFu
  • 使用服务主体对 GetReportInGroupAsync PowerBI Embedded API 调用进行未经授权的响应

    我正在尝试将 Power BI 报告嵌入到我的 Net Core 应用程序中 但是我无法从请求中获得有效的响应 我正在使用 Microsoft PowerBI API 包和通过服务主体注册的 azure 应用程序 据我所知 我已按照此处的说
  • 在没有包装器的情况下在 QT 上使用 OpenGL [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个与 OpenGL 结合使用的 GUI 工具包 QT 看起来很有前途 但不幸的是它将大多数
  • 在 Angular 中旋转伪 3D 轮播

    我正在尝试创建一个伪 3d 轮播 其中包含 5 个项目 如下所示 并让它们循环 我发现这很棒堆栈闪电战 https stackblitz com edit angular ivy sqt1uq file src 2Fapp 2Fapp co
  • 一般替换 Angular 2 路由参数并导航

    我正在构建一个 Angular 应用程序 其中大多数路由都属于给定项目并包含一个projectId 顶部导航区域将有一个项目的下拉列表 当用户从下拉列表中选择一个项目时 它需要导航到当前路线 但将 projectId 替换为新值 这非常类似
  • NameError:未定义全局名称“numpy”

    我正在尝试通过收集 essentia 一个 MIR 库 函数来编写一个特征提取器 流程图如下 单独的特征提取 池化 PoolAggregator 使用poolAggregator连接形成整个特征列表np concatenate 即使不导入
  • 如果屏幕上显示多个二维码,请扫描二维码

    我正在开发一个Android示例应用程序作为一个学术项目 我需要扫描二维码 我使用了二维码扫描库 如果屏幕上只有一个二维码 我能够成功检测到二维码 是的 我想扫描二维码 尽管相同的二维码一次显示在屏幕上的多个位置 例如 在我的笔记本电脑屏幕
  • Nginx:何时使用 proxy_set_header 主机 $host 与 $proxy_host

    我一直在阅读反向代理 想知道什么时候proxy set header Host host是适当的proxy set header Host proxy host 我做了一些研究本文 https www digitalocean com co
  • 在强制布局上拖动会阻止其他 mouseup 侦听器

    我想在 d3 js 强制布局中启用拖动 当拖动一个圆圈并释放鼠标按钮时 我想通过回调调用特定的函数 如下所示 this force d3 layout force nodes this nodes size this width this
  • Netty websocket客户端闲置5分钟后不从服务器读取新帧

    我在服务器端和客户端都使用 Netty 来建立和控制 websocket 连接 我在服务器端有一个空闲状态处理程序 http netty io 4 1 api io netty handler timeout IdleStateHandle