目录
6. ChannelHandler和ChannelPipeline
1. 什么是Netty
1.1 简单理解
想象一下你要开一家在线外卖店:
传统方式(原生Java Socket):
- 你需要亲自接电话、记录订单、安排配送
- 每个客户都要你亲自处理,效率很低
- 处理过程中容易出错,需要大量重复工作
使用Netty:
- 就像有了一套完整的外卖管理系统
- 自动接收订单、智能分配任务、高效处理
- 你只需要关注菜品制作(核心业务逻辑)
1.2 官方定义
Netty是一个基于NIO的客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如协议服务器和客户端。它极大地简化了网络编程,例如TCP和UDP套接字服务器的开发。
1.3 核心特点
// Netty让复杂的网络编程变得简单
public class SimpleNettyServer {
public static void main(String[] args) {
// 只需要几行代码就能创建一个高性能服务器
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MyChannelInitializer());
bootstrap.bind(8080); // 绑定端口,服务器就启动了!
}
}
主要优势:
- 🚀 高性能:基于NIO,支持数十万并发连接
- 🛡️ 稳定可靠:经过大量项目验证,内存管理优秀
- 🔧 易于使用:API设计简洁,开发效率高
- 🎯 功能丰富:内置多种协议支持和编解码器
2. 为什么需要Netty
2.1 传统IO模型的问题
阻塞IO(BIO)的痛点
// 传统BIO服务器的问题
public class TraditionalBIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
// 问题1:accept()会阻塞,同时只能处理一个连接
Socket clientSocket = serverSocket.accept();
// 问题2:每个连接都需要一个线程
new Thread(() -> {
try {
handleClient(clientSocket); // 处理客户端请求
} catch (IOException e) {
e.printStackTrace();
}
}).start(); // 问题3:线程创建销毁开销大
}
}
private static void handleClient(Socket clientSocket) throws IOException {
BufferedReader reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
String line;
while ((line = reader.readLine()) != null) { // 问题4:读取也会阻塞
System.out.println("收到消息: " + line);
}
}
}
BIO的问题总结:
- 一连接一线程:1万个连接需要1万个线程
- 线程开销大:创建、切换、销毁都消耗资源
- 阻塞等待:线程大部分时间在等待IO
- 内存消耗:每个线程默认占用1MB内存
NIO的复杂性
// 原生NIO代码复杂难懂
public class RawNIOServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // 等待事件
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
// 处理连接事件
handleAccept(serverChannel, selector);
} else if (key.isReadable()) {
// 处理读事件
handleRead(key);
}
}
}
}
// 大量样板代码...
}
原生NIO的问题:
- 代码复杂,容易出错
- 需要处理很多底层细节
- 半包、粘包问题需要自己解决
- 跨平台兼容性问题
2.2 Netty的解决方案
// Netty让一切变得简单
public class NettyServer {
public static void main(String[] args) {
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) {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new MyServerHandler());
}
});
ChannelFuture future = bootstrap.bind(8080).sync();
System.out.println("服务器启动成功,端口:8080");
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
// 业务处理器
class MyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String message = (String) msg;
System.out.println("收到消息: " + message);
// 回复消息
ctx.writeAndFlush("服务器收到: " + message);
}
}
Netty的优势:
- ✅ 代码简洁清晰
- ✅ 高性能,支持数十万并发
- ✅ 自动处理半包粘包
- ✅ 丰富的编解码器
- ✅ 优秀的内存管理
3. Netty核心概念
3.1 整体架构图
客户端请求 → Bootstrap → Channel → ChannelPipeline → ChannelHandler
↓
EventLoopGroup → EventLoop → 处理IO事件
↓
ByteBuf ← 数据传输 ← Future/Promise
3.2 Channel(通道)
生活比喻:Channel就像电话线,负责连接双方进行通信。
// Channel的基本概念
public class ChannelExample {
public void channelBasics() {
// Channel代表一个网络连接
// 可以是客户端连接,也可以是服务端连接
// 不同类型的Channel
NioSocketChannel clientChannel; // 客户端TCP连接
NioServerSocketChannel serverChannel; // 服务端TCP监听
NioDatagramChannel udpChannel; // UDP连接
// Channel的生命周期
// ChannelRegistered → ChannelActive → ChannelInactive → ChannelUnregistered
}
}
// Channel使用示例
public class ChannelOperationExample extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
Channel channel = ctx.channel();
// 获取连接信息
SocketAddress localAddress = channel.localAddress();
SocketAddress remoteAddress = channel.remoteAddress();
System.out.println("新连接建立:");
System.out.println("本地地址: " + localAddress);
System.out.println("远程地址: " + remoteAddress);
// 发送欢迎消息
channel.writeAndFlush("欢迎连接到服务器!");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Channel channel = ctx.channel();
// 检查连接是否还活跃
if (channel.isActive()) {
System.out.println("处理消息: " + msg);
// 异步写入数据
ChannelFuture future = channel.writeAndFlush("服务器回复: " + msg);
// 添加监听器处理写入结果
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
System.out.println("消息发送成功");
} else {
System.out.println("消息发送失败: " + future.cause());
}
}
});
}
}
}
3.3 EventLoop(事件循环)
生活比喻:EventLoop就像餐厅服务员,不断地巡视桌子,看哪桌有需要就去处理。
// EventLoop工作原理演示
public class EventLoopExample {
public static void main(String[] args) {
// 创建EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup(4); // 4个EventLoop
try {
// 每个EventLoop可以处理多个Channel
for (int i = 0; i < 10; i++) {
final int channelId = i;
// 提交任务到EventLoop
group.next().execute(() -> {
System.out.println("处理Channel " + channelId +
" 的任务,线程: " + Thread.currentThread().getName());
});
}
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
// EventLoop的任务调度
public class EventLoopTaskExample {
private EventLoopGroup group = new NioEventLoopGroup();
public void scheduleTask() {
EventLoop eventLoop = group.next();
// 延迟执行任务
eventLoop.schedule(() -> {
System.out.println("延迟任务执行了");
}, 5, TimeUnit.SECONDS);
// 周期性执行任务
eventLoop.scheduleAtFixedRate(() -> {
System.out.println("周期性任务: " + new Date());
}, 0, 2, TimeUnit.SECONDS);
}
}
3.4 ChannelHandler(处理器)
生活比喻:ChannelHandler就像工厂流水线上的工人,每个人负责一道工序。
// 入站处理器(处理接收到的数据)
public class MyInboundHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("入站处理器收到消息: " + msg);
// 处理消息(比如解密、解压等)
String processedMsg = "处理后的消息: " + msg;
// 传递给下一个处理器
ctx.fireChannelRead(processedMsg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println("发生异常: " + cause.getMessage());
ctx.close(); // 关闭连接
}
}
// 出站处理器(处理要发送的数据)
public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
System.out.println("出站处理器准备发送: " + msg);
// 处理消息(比如加密、压缩等)
String processedMsg = "加密后的消息: " + msg;
// 传递给下一个处理器
ctx.write(processedMsg, promise);
}
}
// 双向处理器
public class MyDuplexHandler extends ChannelDuplexHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("双向处理器读取: " + msg);
ctx.fireChannelRead(msg);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
System.out.println("双向处理器写入: " + msg);
ctx.write(msg, promise);
}
}
3.5 ChannelPipeline(处理器链)
生活比喻:ChannelPipeline就像工厂的流水线,数据按顺序经过每道工序。
public class PipelineExample {
public void setupPipeline(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 入站方向:从上到下执行
pipeline.addLast("decoder", new StringDecoder()); // 1. 解码
pipeline.addLast("logger", new LoggingHandler()); // 2. 日志
pipeline.addLast("business", new BusinessHandler()); // 3. 业务处理
// 出站方向:从下到上执行
pipeline.addLast("encoder", new StringEncoder()); // 4. 编码
}
}
// 数据流向演示
public class DataFlowHandler extends ChannelInboundHandlerAdapter {
private String name;
public DataFlowHandler(String name) {
this.name = name;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println(name + " 处理入站数据: " + msg);
// 继续传递给下一个处理器
ctx.fireChannelRead(msg);
// 如果要发送数据,会走出站流程
if ("Handler3".equals(name)) {
ctx.writeAndFlush("回复消息");
}
}
}
// 完整的Pipeline示例
public class CompletePipelineExample {
public static void main(String[] args) {
EmbeddedChannel channel = new EmbeddedChannel();
// 构建处理器链
channel.pipeline()
.addLast("handler1", new DataFlowHandler("Handler1"))
.addLast("handler2", new DataFlowHandler("Handler2"))
.addLast("handler3", new DataFlowHandler("Handler3"));
// 模拟接收数据
channel.writeInbound("测试消息");
// 读取响应
Object response = channel.readOutbound();
System.out.println("最终响应: " + response);
}
}
3.6 Bootstrap(启动器)
生活比喻:Bootstrap就像开店的准备工作,配置好所有必要的设置。
// 服务端启动器
public class ServerBootstrapExample {
public void startServer() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 处理连接
EventLoopGroup workerGroup = new NioEventLoopGroup(8); // 处理数据
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap
.group(bossGroup, workerGroup) // 设置线程组
.channel(NioServerSocketChannel.class) // 设置通道类型
.option(ChannelOption.SO_BACKLOG, 128) // 设置连接队列大小
.childOption(ChannelOption.SO_KEEPALIVE, true) // 保持连接活跃
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
// 配置处理器链
ch.pipeline().addLast(new MyServerHandler());
}
});
// 绑定端口并启动服务器
ChannelFuture future = bootstrap.bind(8080).sync();
System.out.println("服务器启动成功,监听端口: 8080");
// 等待服务器关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
// 客户端启动器
public class ClientBootstrapExample {
public void connectToServer() {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap
.group(group) // 设置线程组
.channel(NioSocketChannel.class) // 设置通道类型
.option(ChannelOption.SO_KEEPALIVE, true) // 保持连接
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new MyClientHandler());
}
});
// 连接服务器
ChannelFuture future = bootstrap.connect("localhost", 8080).sync();
System.out.println("连接服务器成功");
// 发送消息
Channel channel = future.channel();
channel.writeAndFlush("Hello Server!");
// 等待连接关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
4. 环境搭建和第一个程序
4.1 环境准备
Maven依赖:
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.94.Final</version>
</dependency>
<!-- 日志依赖(可选) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.8</version>
</dependency>
</dependencies>
Gradle依赖:
dependencies {
implementation 'io.netty:netty-all:4.1.94.Final'
implementation 'ch.qos.logback:logback-classic:1.4.8'
}
4.2 第一个Echo服务器
// 服务端主类
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws InterruptedException {
// 创建两个EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 接受连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理连接
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
// 添加处理器到pipeline
ch.pipeline().addLast(new EchoServerHandler());
}
});
// 绑定端口,开始接受连接
ChannelFuture future = bootstrap.bind().sync();
System.out.println("Echo服务器启动成功,监听端口: " + port);
// 等待服务器socket关闭
future.channel().closeFuture().sync();
} finally {
// 优雅关闭
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoServer(8080).start();
}
}
// 服务端处理器
@ChannelHandler.Sharable // 标记这个处理器可以被多个Channel共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
// 读取接收到的数据
String message = in.toString(CharsetUtil.UTF_8);
System.out.println("服务器收到: " + message);
// 原样返回给客户端(Echo)
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
// 将挂起的消息冲刷到远程节点,并关闭该Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 打印异常堆栈跟踪
cause.printStackTrace();
// 关闭该Channel
ctx.close();
}
}
4.3 第一个Echo客户端
// 客户端主类
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new EchoClientHandler());
}
});
// 连接到远程节点,阻塞等待直到连接完成
ChannelFuture future = bootstrap.connect().sync();
System.out.println("客户端连接成功");
// 阻塞,直到Channel关闭
future.channel().closeFuture().sync();
} finally {
// 关闭线程组并释放所有资源
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient("localhost", 8080).start();
}
}
// 客户端处理器
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 当连接建立时,发送一条消息
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Netty!", CharsetUtil.UTF_8));
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
// 接收服务器的回显消息
String received = msg.toString(CharsetUtil.UTF_8);
System.out.println("客户端收到: " + received);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
4.4 运行和测试
// 测试步骤
public class EchoTest {
public static void main(String[] args) {
// 1. 启动服务器
new Thread(() -> {
try {
new EchoServer(8080).start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 2. 等待服务器启动
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 3. 启动多个客户端测试
for (int i = 0; i < 3; i++) {
final int clientId = i;
new Thread(() -> {
try {
System.out.println("启动客户端 " + clientId);
new EchoClient("localhost", 8080).start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
5. Channel详解
5.1 Channel的生命周期
public class ChannelLifecycleHandler extends ChannelInboundHandlerAdapter {
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
System.out.println("1. Handler被添加到Pipeline");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) {
System.out.println("2. Channel已注册到EventLoop");
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("3. Channel已激活,连接建立成功");
// 连接建立后的初始化工作
Channel channel = ctx.channel();
System.out.println("连接信息:");
System.out.println("- 本地地址: " + channel.localAddress());
System.out.println("- 远程地址: " + channel.remoteAddress());
System.out.println("- 是否打开: " + channel.isOpen());
System.out.println("- 是否激活: " + channel.isActive());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("4. 接收到数据: " + msg);
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
System.out.println("5. 数据读取完成");
ctx.fireChannelReadComplete();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("6. Channel已失活,连接断开");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) {
System.out.println("7. Channel已从EventLoop注销");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
System.out.println("8. Handler从Pipeline移除");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println("异常发生: " + cause.getMessage());
ctx.close(); // 关闭连接
}
}
5.2 Channel的配置选项
public class ChannelOptionsExample {
public void configureServerChannel() {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
// 服务端Channel选项
.option(ChannelOption.SO_BACKLOG, 128) // 连接队列大小
.option(ChannelOption.SO_REUSEADDR, true) // 地址重用
// 客户端Channel选项(childOption)
.childOption(ChannelOption.SO_KEEPALIVE, true) // 保持连接活跃
.childOption(ChannelOption.TCP_NODELAY, true) // 禁用Nagle算法
.childOption(ChannelOption.SO_RCVBUF, 32 * 1024) // 接收缓冲区大小
.childOption(ChannelOption.SO_SNDBUF, 32 * 1024) // 发送缓冲区大小
.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000); // 连接超时
}
public void configureClientChannel() {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
// 客户端选项
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.option(ChannelOption.SO_RCVBUF, 64 * 1024)
.option(ChannelOption.SO_SNDBUF, 64 * 1024);
}
}
5.3 Channel的操作方法
public class ChannelOperationsExample extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
Channel channel = ctx.channel();
// 1. 基本信息查询
System.out.println("Channel信息:");
System.out.println("ID: " + channel.id());
System.out.println("本地地址: " + channel.localAddress());
System.out.println("远程地址: " + channel.remoteAddress());
System.out.println("是否打开: " + channel.isOpen());
System.out.println("是否活跃: " + channel.isActive());
System.out.println("是否可写: " + channel.isWritable());
// 2. 写入操作
demonstrateWriteOperations(channel);
// 3. 异步操作
demonstrateAsyncOperations(channel);
// 4. 属性操作
demonstrateAttributeOperations(channel);
}
private void demonstrateWriteOperations(Channel channel) {
// 写入但不刷新
channel.write("消息1");
channel.write("消息2");
channel.flush(); // 统一刷新
// 写入并刷新
channel.writeAndFlush("消息3");
// 使用ByteBuf
ByteBuf buffer = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8);
channel.writeAndFlush(buffer);
}
private void demonstrateAsyncOperations(Channel channel) {
// 异步写入
ChannelFuture future = channel.writeAndFlush("异步消息");
// 添加监听器
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
System.out.println("消息发送成功");
} else {
System.out.println("消息发送失败: " + future.cause());
}
}
});
// 或者使用Lambda表达式
future.addListener((ChannelFutureListener) f -> {
if (f.isSuccess()) {
System.out.println("发送成功");
} else {
System.out.println("发送失败: " + f.cause());
}
});
}
private void demonstrateAttributeOperations(Channel channel) {
// 定义属性键
AttributeKey<String> USER_ID = AttributeKey.valueOf("userId");
AttributeKey<Date> LOGIN_TIME = AttributeKey.valueOf("loginTime");
// 设置属性
channel.attr(USER_ID).set("user123");
channel.attr(LOGIN_TIME).set(new Date());
// 获取属性
String userId = channel.attr(USER_ID).get();
Date loginTime = channel.attr(LOGIN_TIME).get();
System.out.println("用户ID: " + userId);
System.out.println("登录时间: " + loginTime);
}
}
5.4 不同类型的Channel
// TCP服务端Channel
public class TcpServerChannelExample {
public void createTcpServer() {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.channel(NioServerSocketChannel.class); // TCP服务端
// 配置TCP特有选项
bootstrap.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true);
}
}
// TCP客户端Channel
public class TcpClientChannelExample {
public void createTcpClient() {
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class); // TCP客户端
bootstrap.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true);
}
}
// UDP Channel
public class UdpChannelExample {
public void createUdpServer() {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioDatagramChannel.class) // UDP
.handler(new UdpServerHandler());
// UDP特有选项
bootstrap.option(ChannelOption.SO_BROADCAST, true)
.option(ChannelOption.SO_RCVBUF, 64 * 1024);
}
}
// UDP处理器
public class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) {
// 读取UDP数据包
ByteBuf content = msg.content();
String message = content.toString(CharsetUtil.UTF_8);
InetSocketAddress sender = msg.sender();
System.out.println("收到UDP消息: " + message + " 来自: " + sender);
// 回复UDP消息
String response = "服务器收到: " + message;
ByteBuf responseBuf = Unpooled.copiedBuffer(response, CharsetUtil.UTF_8);
DatagramPacket responsePacket = new DatagramPacket(responseBuf, sender);
ctx.writeAndFlush(responsePacket);
}
}
6. ChannelHandler和ChannelPipeline
6.1 ChannelHandler类型详解
// 入站处理器 - 处理读取数据
public class CustomInboundHandler extends ChannelInboundHandlerAdapter {
private String name;
public CustomInboundHandler(String name) {
this.name = name;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("[" + name + "] 入站处理: " + msg);
// 修改消息内容
String message = msg.toString();
String processedMessage = "[" + name + "处理] " + message;
// 传递给下一个处理器
ctx.fireChannelRead(processedMessage);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("[" + name + "] 连接激活");
ctx.fireChannelActive();
}
}
// 出站