阶段二:客户端实现
在这个阶段,我们将学习如何使用Netty实现客户端,理解客户端与服务端的区别。
项目 1-07:实现基础Netty客户端
📌 项目目标
学习如何使用Netty实现客户端,理解客户端与服务端的区别。实现最简单的Netty客户端,只有连接功能。
🔑 核心知识点
-
Bootstrap:客户端启动辅助类(服务端使用ServerBootstrap)
-
客户端只需要一个EventLoopGroup:
- 服务端需要两个:Boss和Worker
- 客户端只需要一个:Worker
-
connect():客户端连接服务端(服务端使用bind())
-
客户端与服务端的区别:
- 启动类:Bootstrap vs ServerBootstrap
- 线程组:1个 vs 2个
- Channel类型:NioSocketChannel vs NioServerSocketChannel
- 操作:connect() vs bind()
💻 核心代码解析
NettyClient.java - 客户端启动类
private void connect(String host, int port) {
// 1. 创建一个线程组(客户端只需要一个)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 2. 创建客户端启动器
Bootstrap b = new Bootstrap();
b.group(workerGroup)
.channel(NioSocketChannel.class) // 使用NioSocketChannel
.option(ChannelOption.AUTO_READ, true)// 自动读取数据
.handler(new MyChannelInitializer());
// 3. 连接服务端
ChannelFuture f = b.connect(host, port).sync();
System.out.println("客户端连接成功");
// 4. 等待客户端Channel关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 5. 优雅关闭线程组
workerGroup.shutdownGracefully();
}
}
MyChannelInitializer.java - Channel初始化器
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) {
// 此时Pipeline中没有添加任何Handler
// 所以客户端只能连接,不能收发数据
}
}
🎯 运行效果
- 客户端能够成功连接服务端
- 但不能发送和接收数据
📊 客户端与服务端对比
| 特性 | 服务端 | 客户端 |
|---|---|---|
| 启动类 | ServerBootstrap | Bootstrap |
| 线程组数量 | 2个(Boss+Worker) | 1个(Worker) |
| Channel类型 | NioServerSocketChannel | NioSocketChannel |
| 启动方法 | bind(port) | connect(host, port) |
| 作用 | 监听端口,接收连接 | 主动连接服务端 |
项目 1-08:实现完整的收发功能
📌 项目目标
实现完整的Netty客户端,实现连接+收发数据功能。
🔑 核心知识点
-
客户端编解码器配置:与服务端相同
-
客户端发送数据:
- 在channelActive()中发送(连接建立后)
- 在channelRead()中发送(收到消息后)
-
完整的客户端功能:连接、发送、接收
💻 核心代码解析
MyChannelInitializer.java - 添加编解码器
@Override
protected void initChannel(SocketChannel channel) {
// 解码器(入站)
channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
// 编码器(出站)
channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
// 业务处理器
channel.pipeline().addLast(new MyClientHandler());
}
MyClientHandler.java - 客户端业务处理器
public class MyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 连接建立后,主动发送消息
SocketChannel channel = (SocketChannel) ctx.channel();
System.out.println("连接服务端:" + channel.localAddress().getHostString());
// 发送消息
ctx.writeAndFlush("你好,我是客户端\r\n");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 接收服务端消息
String message = (String) msg;
System.out.println("收到服务端消息:" + message);
// 可以继续发送消息
ctx.writeAndFlush("收到你的消息了\r\n");
}
}
🎯 运行效果
- 客户端连接服务端后,主动发送消息
- 客户端能够接收服务端的响应
- 实现了完整的双向通信
📊 与项目1-07的对比
| 功能 | 项目1-07 | 项目1-08 |
|---|---|---|
| 连接服务端 | ✅ | ✅ |
| 发送数据 | ❌ | ✅ |
| 接收数据 | ❌ | ✅ |
| 编解码器 | 无 | 有 |
💡 客户端开发要点
- 客户端通常在channelActive()中主动发送消息
- 客户端和服务端使用相同的编解码器
- 客户端可以实现自动重连机制
1748



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



