💬 项目五:WebSocket 实时聊天系统
项目概述
项目名称:itstack-demo-netty-2-05
核心功能:基于WebSocket实现实时聊天系统,模仿微信聊天界面
技术要点:WebSocket协议、HTTP握手、ChannelGroup群发、JSP前端
为什么需要这个项目?
实际问题:
- HTTP是单向通信,服务器无法主动推送消息
- 轮询方式浪费资源,实时性差
- 需要浏览器与服务器实时双向通信
- 需要实现群发消息功能
解决方案:
- ✅ WebSocket长连接:一次握手,持续通信
- ✅ 双向通信:服务器可以主动推送消息
- ✅ ChannelGroup:轻松实现消息群发
- ✅ 低开销:握手后只传输数据,无HTTP头
适用场景:
- 即时通讯(在线客服、企业聊天)
- 实时推送(股票行情、新闻资讯)
- 协同办公(在线文档、白板共享)
- 游戏(多人在线游戏、实时对战)
核心知识点
1. WebSocket vs HTTP
| 特性 | HTTP | WebSocket |
|---|---|---|
| 连接方式 | 短连接(请求-响应) | 长连接(持久连接) |
| 通信方向 | 单向(客户端→服务器) | 双向(客户端↔服务器) |
| 实时性 | 差(需要轮询) | 好(服务器主动推送) |
| 开销 | 大(每次请求都有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());
}
}
三个核心编解码器:
| 编解码器 | 作用 |
|---|---|
HttpServerCodec | HTTP编解码器,处理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 | 二进制消息 | 文件传输 |
PingWebSocketFrame | Ping消息 | 心跳检测 |
PongWebSocketFrame | Pong消息 | 心跳响应 |
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:实现消息群发
- ✅ 协议设计:清晰的客户端/服务端消息协议
适用场景:
- 即时通讯(在线客服、企业聊天)
- 实时推送(股票行情、新闻资讯)
- 协同办公(在线文档、白板共享)
- 游戏(多人在线游戏、实时对战)
5186

被折叠的 条评论
为什么被折叠?



