基于Netty构建WebSocket服务器实战指南

一、WebSocket服务器基础概念

1.1 WebSocket简介

WebSocket是一种在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据,解决了HTTP协议只能由客户端发起请求的问题。

1.2 Netty框架优势

  • 高性能:基于NIO的异步事件驱动模型
  • 可扩展性:灵活的管道机制,易于扩展
  • 成熟稳定:被广泛应用在各种高并发场景

二、核心代码解析

2.1 服务器初始化

java 复制代码
@PostConstruct
public void init() {
    new Thread(() -> {
        try {
            start();
        } catch (InterruptedException e) {
            log.error("启动WebSocket服务器失败!", e);
        }
    }).start();
}

使用@PostConstruct注解确保Spring容器初始化完成后自动启动WebSocket服务器。

2.2 线程组配置

java 复制代码
// 主线程组:处理客户端连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 工作线程组:处理连接后的I/O操作
EventLoopGroup workGroup = new NioEventLoopGroup();
  • bossGroup:负责接受新连接
  • workGroup:负责处理已建立连接的I/O操作

2.3 ChannelPipeline配置

java 复制代码
pipeline.addLast(new HttpServerCodec());           // HTTP编解码器
pipeline.addLast(new HttpObjectAggregator(65535)); // HTTP消息聚合器
pipeline.addLast(new WebSocketServerProtocolHandler("/testWs")); // WebSocket协议处理器

三、实际应用案例

3.1 场景:实时聊天室

案例背景

假设我们需要构建一个简单的实时聊天室,用户可以加入聊天室并实时接收其他用户发送的消息。

服务器端实现要点
  • 使用ChannelGroup管理所有连接的客户端
  • handlerAdded()方法中添加新连接到群组
  • handlerRemoved()方法中移除断开的连接
客户端HTML示例
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>WebSocket聊天室</title>
</head>
<body>
    <div id="messages"></div>
    <input type="text" id="messageInput" placeholder="输入消息...">
    <button onclick="sendMessage()">发送</button>

    <script>
        const ws = new WebSocket('ws://localhost:9090/testWs');
        
        ws.onopen = function(event) {
            console.log('连接已建立');
        };
        
        ws.onmessage = function(event) {
            const messages = document.getElementById('messages');
            messages.innerHTML += '<p>' + event.data + '</p>';
        };
        
        function sendMessage() {
            const input = document.getElementById('messageInput');
            ws.send(input.value);
            input.value = '';
        }
    </script>
</body>
</html>

3.2 消息处理逻辑

java 复制代码
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
    Channel channel = ctx.channel();
    log.info("收到来自通道channelId[{}]发送的消息:{}", channel.id(), msg.text());

    // 回复确认消息给发送者
    ctx.writeAndFlush(new TextWebSocketFrame("收到您的消息:" + msg.text()));

    // 广播消息给其他所有客户端(注释部分)
    // channelGroup.writeAndFlush(new TextWebSocketFrame("来自[" + channel.id() + "]的消息:" + msg.text()));
}

四、功能特性说明

4.1 连接管理

  • 连接建立 :通过handlerAdded()捕获新连接并添加到ChannelGroup
  • 连接断开 :通过handlerRemoved()清理断开的连接
  • 连接监控:记录连接状态变化日志

4.2 消息处理

  • 单播:只回复给发送消息的客户端
  • 广播:可以向所有连接的客户端发送消息(需取消注释相关代码)

4.3 错误处理

  • 异常捕获:在init()方法中捕获InterruptedException
  • 日志记录:详细记录连接状态和消息处理过程

五、部署与测试

5.1 启动服务

  • Spring Boot应用启动后会自动初始化WebSocket服务器
  • 服务器监听端口:9090
  • WebSocket路径:/testWs

5.2 测试验证

  1. 打开多个浏览器窗口访问客户端页面
  2. 在任意窗口发送消息
  3. 观察其他窗口是否能收到对应消息
  4. 查看控制台日志验证连接状态

六、优化建议

6.1 性能优化

  • 根据服务器性能调整NioEventLoopGroup线程数
  • 合理设置消息聚合器大小
  • 实现心跳机制防止连接超时

6.2 安全考虑

  • 添加身份验证机制
  • 实现消息过滤和防攻击机制
  • 考虑使用WSS(WebSocket Secure)协议

这个WebSocket服务器为实时通信提供了坚实的基础架构,可根据具体业务需求进行定制化开发。

java 复制代码
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
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.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * WebSocket服务器类
 */
@Getter
@Slf4j
@Component
public class WebSocketServer {

    // 管理所有WebSocket连接的通道组
    private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @PostConstruct
    public void init() {
        new Thread(() -> {
            try {
                start();
            } catch (InterruptedException e) {
                log.error("启动WebSocket服务器失败!", e);
            }

        }).start();
    }

    /**
     * 启动WebSocket服务器
     */
    public void start() throws InterruptedException {
        // 主线程组:处理客户端连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 工作线程组:处理连接后的I/O操作
        EventLoopGroup workGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workGroup);
        serverBootstrap.channel(NioServerSocketChannel.class);
        serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

            @Override
            protected void initChannel(SocketChannel socketChannel) {
                ChannelPipeline pipeline = socketChannel.pipeline();
                // HTTP编解码器
                pipeline.addLast(new HttpServerCodec());
                // HTTP消息聚合器
                pipeline.addLast(new HttpObjectAggregator(65535));
                // WebSocket协议处理器,路径为/testWs
                pipeline.addLast(new WebSocketServerProtocolHandler("/testWs"));

                // 自定义业务处理器
                pipeline.addLast(new SimpleChannelInboundHandler<TextWebSocketFrame>() {

                    @Override
                    public void handlerAdded(ChannelHandlerContext ctx) {
                        // 客户端连接建立
                        Channel channel = ctx.channel();
                        log.info("客户端 建立连接:channelId={}", channel.id());
                        channelGroup.add(channel);
                    }

                    @Override
                    public void handlerRemoved(ChannelHandlerContext ctx) {
                        // 客户端连接断开
                        Channel channel = ctx.channel();
                        log.info("客户端 断开连接:channelId={}", channel.id());
                        channelGroup.remove(channel);
                    }

                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
                        // 处理收到的文本消息
                        Channel channel = ctx.channel();
                        log.info("收到来自通道channelId[{}]发送的消息:{}", channel.id(), msg.text());

                        // 回复消息给发送者
                        ctx.writeAndFlush(new TextWebSocketFrame("收到您的消息:" + msg.text()));

                        // 广播消息给所有客户端
                        //channelGroup.writeAndFlush( new TextWebSocketFrame("收到来自通道channelId[" + channel.id() + "]发送的消息:" + msg.text()));
                    }
                });
            }
        });

        // 绑定端口并启动服务器
        ChannelFuture channelFuture = serverBootstrap.bind(9090).sync();
        log.info("Server started and listen on:{}", channelFuture.channel().localAddress());
        // 等待服务器关闭
        channelFuture.channel().closeFuture().sync();
    }
}

客户端连接地址 ws://127.0.0.1:9090/testWs

6.3 调试

推荐一个WebSocke在线网站

https://webfem.com/tools/ws/index.html

七、总结

基于Netty框架构建的WebSocket服务器,实现了完整的实时通信功能。
核心技术 :采用Netty的NioEventLoopGroup线程模型,通过ChannelGroup管理连接池
协议处理 :配置HTTP编解码器和WebSocket协议处理器,路径为/testWs
功能特性 :支持连接建立/断开监控、单播回复和广播消息、详细日志记录
部署运行:自动随Spring Boot启动,监听端口9090,适用于聊天室、消息推送等实时交互场景

相关推荐
用户83071968408212 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解13 小时前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解13 小时前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记17 小时前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者1 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840821 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解2 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
YuMiao2 天前
gstatic连接问题导致Google Gemini / Studio页面乱码或图标缺失问题
服务器·网络协议
初次攀爬者2 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺2 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端