Netty实战应用学习笔记-中级拓展篇(五)

💬 项目五:WebSocket 实时聊天系统

项目概述

项目名称:itstack-demo-netty-2-05
核心功能:基于WebSocket实现实时聊天系统,模仿微信聊天界面
技术要点:WebSocket协议、HTTP握手、ChannelGroup群发、JSP前端

为什么需要这个项目?

实际问题

  • HTTP是单向通信,服务器无法主动推送消息
  • 轮询方式浪费资源,实时性差
  • 需要浏览器与服务器实时双向通信
  • 需要实现群发消息功能

解决方案

  • ✅ WebSocket长连接:一次握手,持续通信
  • ✅ 双向通信:服务器可以主动推送消息
  • ✅ ChannelGroup:轻松实现消息群发
  • ✅ 低开销:握手后只传输数据,无HTTP头

适用场景

  • 即时通讯(在线客服、企业聊天)
  • 实时推送(股票行情、新闻资讯)
  • 协同办公(在线文档、白板共享)
  • 游戏(多人在线游戏、实时对战)

核心知识点

1. WebSocket vs HTTP
特性HTTPWebSocket
连接方式短连接(请求-响应)长连接(持久连接)
通信方向单向(客户端→服务器)双向(客户端↔服务器)
实时性差(需要轮询)好(服务器主动推送)
开销大(每次请求都有HTTP头)小(握手后只传输数据)
应用场景普通网页浏览聊天、游戏、实时推送

类比理解

  • HTTP:像发短信,你发一条,对方回一条
  • WebSocket:像打电话,一次接通,持续通话
2. Pipeline配置

MyChannelInitializer.java

public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel channel) {
        // 1. HTTP编解码器
        channel.pipeline().addLast("http-codec", new HttpServerCodec());
        
        // 2. HTTP消息聚合器(将多个HTTP消息聚合成一个完整的FullHttpRequest)
        channel.pipeline().addLast("aggregator", 
            new HttpObjectAggregator(65536));
        
        // 3. 分块写处理器(支持大文件传输)
        channel.pipeline().addLast("http-chunked", 
            new ChunkedWriteHandler());
        
        // 4. 自定义业务处理器
        channel.pipeline().addLast(new My
ServerHandler());
    }
}

三个核心编解码器

编解码器作用
HttpServerCodecHTTP编解码器,处理HTTP请求和响应
HttpObjectAggregator将HTTP消息的多个部分聚合成完整的FullHttpRequest
ChunkedWriteHandler支持异步发送大文件,防止内存溢出
3. WebSocket握手处理

MyServerHandler.java

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    // 处理HTTP握手请求
    if (msg instanceof FullHttpRequest) {
        FullHttpRequest httpRequest = (FullHttpRequest) msg;
        
        // 1. 检查HTTP请求是否有效
        if (!httpRequest.decoderResult().isSuccess()) {
            // 返回400错误
            DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST
            );
            ctx.channel().writeAndFlush(httpResponse);
            return;
        }
        
        // 2. 创建WebSocket握手工厂
        WebSocketServerHandshakerFactory wsFactory = 
            new WebSocketServerHandshakerFactory(
                "ws:/" + ctx.channel() + "/websocket", null, false
            );
        
        // 3. 执行握手
        handshaker = wsFactory.newHandshaker(httpRequest);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(
                ctx.channel()
            );
        } else {
            handshaker.handshake(ctx.channel(), httpRequest);
        }
        return;
    }
    
    // 处理WebSocket消息
    if (msg instanceof WebSocketFrame) {
        handleWebSocketFrame(ctx, (WebSocketFrame) msg);
    }
}

WebSocket握手流程

【客户端】                          【服务端】
    |                                  |
    | 1. 发送HTTP升级请求                |
    |  (Upgrade: websocket)            |
    |--------------------------------->|
    |                                  | 2. 验证请求
    |                                  | 3. 返回101状态码
    | 4. 接收握手响应                    |    (Switching Protocols)
    |<---------------------------------|
    | 5. 连接建立,开始WebSocket通信      |
4. WebSocket消息处理

处理不同类型的WebSocket帧

private void handleWebSocketFrame(ChannelHandlerContext ctx, 
                                  WebSocketFrame frame) {
    // 1. 处理关闭请求
    if (frame instanceof CloseWebSocketFrame) {
        handshaker.close(ctx.channel(), 
            (CloseWebSocketFrame) frame.retain());
        return;
    }
    
    // 2. 处理Ping请求(心跳检测)
    if (frame instanceof PingWebSocketFrame) {
        ctx.channel().write(
            new PongWebSocketFrame(frame.content().retain())
        );
        return;
    }
    
    // 3. 只支持文本消息
    if (!(frame instanceof TextWebSocketFrame)) {
        throw new Exception("仅支持文本格式");
    }
    
    // 4. 解析消息
    String request = ((TextWebSocketFrame) frame).text();
    ClientMsgProtocol clientMsg = JSON.parseObject(request, 
        ClientMsgProtocol.class);
    
    // 5. 根据消息类型处理
    if (1 == clientMsg.getType()) {
        // 请求个人信息
        ctx.channel().writeAndFlush(
            MsgUtil.buildMsgOwner(ctx.channel().id().toString())
        );
    } else if (2 == clientMsg.getType()) {
        // 群发消息
        TextWebSocketFrame textFrame = MsgUtil.buildMsgAll(
            ctx.channel().id().toString(), clientMsg.getMsgInfo()
        );
        ChannelHandler.channelGroup.writeAndFlush(textFrame);
    }
}

WebSocket帧类型

帧类型说明用途
TextWebSocketFrame文本消息聊天消息
BinaryWebSocketFrame二进制消息文件传输
PingWebSocketFramePing消息心跳检测
PongWebSocketFramePong消息心跳响应
CloseWebSocketFrame关闭消息关闭连接
5. ChannelGroup群发消息

ChannelHandler.java

public class ChannelHandler {
    // 用于存放所有在线用户的Channel
    public static ChannelGroup channelGroup = 
        new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}

使用方式

// 添加Channel
@Override
public void channelActive(ChannelHandlerContext ctx) {
    ChannelHandler.channelGroup.add(ctx.channel());
}

// 移除Channel
@Override
public void channelInactive(ChannelHandlerContext ctx) {
    ChannelHandler.channelGroup.remove(ctx.channel());
}

// 群发消息
TextWebSocketFrame textFrame = MsgUtil.buildMsgAll(channelId, msgInfo);
ChannelHandler.channelGroup.writeAndFlush(textFrame);

类比理解

  • ChannelGroup:像一个微信群
  • Channel:群里的每个成员
  • writeAndFlush():群发消息给所有成员
6. 消息协议设计

ClientMsgProtocol.java - 客户端消息

public class ClientMsgProtocol {
    private int type;       // 1请求个人信息,2发送聊天信息
    private String msgInfo; // 消息内容
}

ServerMsgProtocol.java - 服务端消息

public class ServerMsgProtocol {
    private int type;             // 1自发信息、2群发消息
    private String channelId;     // 通信管道ID(用户标识)
    private String userHeadImg;   // 用户头像
    private String msgInfo;       // 消息内容
}

项目总结

核心技术

  • ✅ WebSocket长连接:浏览器与服务器实时双向通信
  • ✅ HTTP握手升级:从HTTP协议升级到WebSocket
  • ✅ ChannelGroup:实现消息群发
  • ✅ 协议设计:清晰的客户端/服务端消息协议

适用场景

  • 即时通讯(在线客服、企业聊天)
  • 实时推送(股票行情、新闻资讯)
  • 协同办公(在线文档、白板共享)
  • 游戏(多人在线游戏、实时对战)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值