Appearance
Netty - 01 入门案例
本文介绍Netty的入门案例以及流程,入门案例主要使用Netty实现服务端监听8080端口,客户端连接到服务端,并且发送"hello,world",服务端接收到消息并打印到控制台。
1. 服务端代码
java
@Slf4j
public class HelloServer {
public static void main(String[] args) {
// 1. 启动器,负责组装netty组件,启动服务器
new ServerBootstrap()
// 2. 指定一个线程组,该线程组用于处理连接事件、读写事件以及其他普通事件
.group(new NioEventLoopGroup())
// 3. 选择服务器的ServerSocketChannel实现
.channel(NioServerSocketChannel.class)
// 4. 指定一个SocketChannel初始化器,当有新的连接建立时,会执行该初始化器的initChannel方法,在该连接上添加处理器
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 6. 有新连接建立时,首先添加一个StringDecoder处理器,该处理器的作用是将ByteBuf转换为String
ch.pipeline().addLast(new StringDecoder());
// 然后添加自定义入栈处理器,该处理器监听读事件,输出消息
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info(msg.toString());
}
});
}
})
// 5. 绑定8080端口
.bind(8080);
}
}2. 客户端代码
java
public class HelloClient {
public static void main(String[] args) throws InterruptedException {
//1.创建启动对象 new Bootstrap()
ChannelFuture channelFuture = new Bootstrap()
// 2. 指定线程组
.group(new NioEventLoopGroup())
// 3. 选择客户端Channel实现
.channel(NioSocketChannel.class)
// 4. 指定一个SocketChannel初始化器,当成功建立连接后,会在SocketChannel上应用该初始化器(调用initChannel方法)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 7. 连接建立完成后,应用该初始化器,在连接上添加StringEncoder处理器,该处理器的作用是将String转换为ByteBuf
ch.pipeline().addLast(new StringEncoder());
}
})
// 5. 建立连接,异步方法返回Future对象
.connect(new InetSocketAddress("127.0.0.1", 8080));
// 6. 阻塞,等待连接建立完成
channelFuture.sync();
// 8. 获取连接
Channel channel = channelFuture.channel();
// 9. 发送数据
channel.writeAndFlush("hello world");
}
}3. 流程分析与组件分析
3.1 流程分析
接下来我们分别从个服务端与客户端的初始化、客户端与服务端建立连接的流程进行分析:
服务端的初始化
- 首先创建服务端的启动器
ServerBootstrap,用于管理各个组件,启动服务器; - 然后指定线程组
NioEventLoopGroup(),在服务端分为主线程(用于接收请求创建连接)和工作线程(用于处理读写事件),在Netty中,这些线程以EventLoop表示,将这些线程放在同一个组里,称为EventLoopGroup,NioEventLoopGroup()是一个实现; - 然后指定服务端的
NioServerSocketChannel,即NIO中的ServerSocketChannel,用于接收请求获取连接; - 指定新连接的初始化器
childHandler中的ChannelInitializer; - 监听端口8080;
客户端的初始化
- 首先创建客户端的启动器
Bootstrap(),用于管理客户端的各个组件; - 然后指定线程组
NioEventLoopGroup(),在客户端也要分线程,一个线程发送数据(主线程),一个线程接收数据(EventLoop); - 指定新连接的初始化器
handler中的ChannelInitializer; - 调用
connect()连接到服务端,该方法是异步方法,返回Future对象; - 调用
Future对象的sync()方法,阻塞等待连接建立; - 调用
Future的channel()方法,获取与服务端的连接;
客户端与服务端的交互
- 首先客户端向服务端请求建立连接;
- 服务端接收到请求后,通过
childHandler中的ChannelInitializer初始化连接,在服务端为该连接增加两个处理器Handler:StringDecoder,StringDecoder的作用是将ByteBuf(字节缓冲区,是Netty对NIO中ByteBuffer的包装)解码为字符串;ChannelInboundHandlerAdapter自定义处理器,接收上一步处理器的结果msg,并接着处理;
- 连接建立后,客户端也通过
handler中的ChannelInitializer初始化连接,在客户端为该连接增加一个处理器——StringEncoder,StringEncoder的作用是将字符串编码为ByteBuf; - 客户端调用
channel.writeAndFlush()发送消息到服务端,字符串消息会经过StringEncoder处理器,将字符串编码为ByteBuf; - 服务端接收到消息后,首先经过第一个处理器
StringDecoder将字节解码为字符串,然后将字符串传递给下一个自定义处理器,即打印出消息;
3.2 组件分析
- EventLoop与EventLoopGroup;
- Pipeline与Handler,Handler分为InBound与OutBound;
- Channel与Future;
- ByteBuf;