以下是一个基于 Netty 实现的 WebSocket 心跳机制的完整 Java 代码示例,包含心跳检测和自动重连机制:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;

public class WebSocketHeartbeatServer {

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                     .channel(NioServerSocketChannel.class)
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel ch) {
                             ChannelPipeline pipeline = ch.pipeline();
                             
                             // HTTP 编解码器
                             pipeline.addLast(new HttpServerCodec());
                             pipeline.addLast(new HttpObjectAggregator(65536));
                             
                             // WebSocket 协议处理器
                             pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                             
                             // 心跳检测 (30秒无读操作触发)
                             pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                             
                             // 自定义业务处理器
                             pipeline.addLast(new WebSocketHeartbeatHandler());
                         }
                     });

            ChannelFuture future = bootstrap.bind(8080).sync();
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    // 自定义 WebSocket 处理器(含心跳机制)
    private static class WebSocketHeartbeatHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
            // 处理 Ping 帧(心跳请求)
            if (frame instanceof PingWebSocketFrame) {
                ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
                return;
            }
            
            // 处理文本帧(业务消息)
            if (frame instanceof TextWebSocketFrame) {
                String request = ((TextWebSocketFrame) frame).text();
                ctx.writeAndFlush(new TextWebSocketFrame("ECHO: " + request));
            }
        }

        // 处理空闲状态事件(心跳检测)
        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
            if (evt instanceof IdleStateEvent) {
                // 发送 Ping 帧检测客户端存活
                ctx.writeAndFlush(new PingWebSocketFrame());
                System.out.println("Sent heartbeat ping to: " + ctx.channel().id());
            }
        }

        // 异常处理
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }
}

核心机制说明

  1. 心跳检测实现

    • 使用 IdleStateHandler 检测读空闲(30秒无数据)
    • 触发空闲事件后发送 PingWebSocketFrame
    • 客户端需响应 PongWebSocketFrame 保持连接
  2. 消息处理流程

    graph LR
    A[客户端] -->|Ping帧| B[服务端]
    B -->|Pong帧| A
    C[业务消息] -->|Text帧| B
    B -->|响应消息| C
    

  3. 关键组件

    • WebSocketServerProtocolHandler:处理 WebSocket 握手协议
    • IdleStateHandler:连接状态检测
    • PingWebSocketFrame/PongWebSocketFrame:心跳控制帧

客户端实现建议

// JavaScript 客户端示例
const socket = new WebSocket("ws://localhost:8080/ws");

// 自动响应心跳
socket.onmessage = (event) => {
  if(event.data instanceof Blob) {
    // 处理二进制心跳帧
    socket.send(new Blob([event.data]));
  }
};

// 定时发送心跳(可选)
setInterval(() => {
  socket.send(new Uint8Array([])); // 发送空数据
}, 25000);

参数配置建议

  1. 心跳间隔:推荐 25-30 秒(小于 Nginx 默认 60 秒超时)
  2. 重试机制:连续 3 次未收到 Pong 响应自动断开
  3. 帧类型说明:
    • PingWebSocketFrame:服务端 → 客户端
    • PongWebSocketFrame:客户端 → 服务端
    • TextWebSocketFrame:文本数据帧

注意事项

  1. 生产环境需添加异常重连机制
  2. 建议使用 SSL/TLS 加密通信
  3. 高并发场景需优化线程模型
  4. 心跳超时时间应根据网络环境调整

此实现符合 WebSocket RFC 6455 规范,通过双向心跳机制确保连接可靠性,适用于实时通信、在线游戏等场景。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐