前言
在学习Netty过程中我编写了一个极其简单的服务器,实现了一个最基本的功能: 客户端向服务器发送一条消息Hello World!,然后在服务器端显示出来。下面来从运行流程方面做一个简析。
话不多说,先上代码:
Server.java
public class Server{
public static void main(String[] args){
// 1
new ServerBootstrap()
// 2
.group(new NioEventLoopGroup())// 15
// 3
.channel(NioServerSocketChannel.class)
// 4
.childHandler(new ChannelInitializer<NioSocketChannel>() {
// 11
@Override
protected void initChannel(NioSocketChannel ch) {
// 16
ch.pipeline().addLast(new StringDecoder());//可以添加多个请求处理工序
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
// 17
@Override
protected void channelRead(ChannelHandlerContext ctx, String msg) {
System.out.println(msg);
}
});
}
})
// 5
.bind(8080);
}
}
Client.java
public class{
public static void main(String[] args){
// 6
new Bootstrap()
// 7
.group(new NioEventLoopGroup())
// 8
.channel(NioSocketChannel.class)
// 9
.handler(new ChannelInitializer<Channel>() {
// 11
@Override
protected void initChannel(Channel ch) {
// 14
ch.pipeline().addLast(new StringEncoder());
}
})
// 10
.connect("127.0.0.1", 8080)
// 12
.sync()
// 13
.channel()//channel()返回读写对象,才能发送或接受消息
.writeAndFlush(new Date() + ": hello world!");
}
}
首先让我们来理解几个概念
- 把 channel 理解为数据的通道
- 把 msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 的加工,会变成其它类型对象,最后输出又变成 ByteBuf。数据从客户端到服务器以及服务器到客户端都是以 ByteBuf 形式传输
- 把 handler 理解为数据的处理器,可以往里面添加很多处理工序(ch.pipeline().addLast()方法)
- 工序有多道,合在一起就是 pipeline,pipeline 负责发布事件(读、读取完成...)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
- handler 分 Inbound 和 Outbound 两类
- 把 eventLoop 理解为处理数据的工人
- 工人可以管理多个 channel 的 io 操作,并且一旦工人负责了某个 channel,就要负责到底(绑定)
- 工人既可以执行 io 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务
- 工人按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每道工序指定不同的工人
执行流程(按照代码中的注释依次执行)
- 启动器,组装 Netty 组件,启动服务器
- 创建 NioEventLoopGroup,可以简单理解为
线程池 + Selector后面会详细展开 - 选择服务端 Scoket 实现类,其中 NioServerSocketChannel 表示基于 NIO 的服务器端实现
- 添加处理器。注: initChannel 方法不在服务器启动时运行,而是在客户端连接服务器时运行
- 绑定监听端口
- 启动客户端
- 创建客户端 EventLoopGroup
- 选择客户端 Socket 实现类
- 添加处理器。注: initChannel 方法不在客户端启动时运行,而是在客户端连接服务器时运行
- 连接到服务器
- 服务器在连接建立后调用 initChannel 初始化方法,客户端同时也调用 initChannel 方法
- sync()是一个同步方法,在连接建立完成前一直阻塞,建立完成后结束以继续后面的流程
- 向服务器发送数据
- 将
Hello World!字符串转为 ByteBuf - 由某个 EventLoop 中的 read() 方法接收 ByteBufg
- 将 ByteBuf 还原为字符串
- 执行下一个处理工序,将从客户端接收到的字符串打印出来
结语
本文仅记录了一个Netty服务器接收一个请求时的流程,错漏之处,欢迎指正。
一个有梦想的小白的成长之路。欢迎光临个人 gitee首页: Alan_ZhangAoYu