下面是大致的流程,配合底下两端的代码注释就会让你醍醐灌顶啦。建议把它们拷到你的IDEA中去运行一下,看得也更清楚一些
先是服务端,类注释是上图的解释
/**
* @author fiji
* @date 2022/3/18 13:56
*
* 在客户端尝试和服务端建立连接后,服务端在启动之后会监听80端口
* 客户端发送连接请求,此时EventLoopGroup选择器正在监听ACCEPT事件,
* 监听到了ACCEPT事件后,就会找一个处理器类去处理这个ACCEPT事件。而处理ACCEPT
* 事件的处理器类是Netty帮助我们实现的,我们现在看不到实现,结果上就是
* 那个处理ACCEPT事件的处理器会去调用我们自己在下面写的初始化器中的initChannel方法
*
* 当然对于客户端也是一样,也是在客户端连接后会去调用客户端的initChannel方法
*
* 我们说回客户端:
* sync方法会在连接之后才会往下运行,它是一个同步方法,或者成为阻塞方法。连接建立之前会阻塞sync下面的代码
* 也就是说sync方法唯一的作用就是等待连接建立好,然后放行下面的方法。等连接建立好之后下面就可以拿到
* channel对象(即执行channel()方法),那么这里的channel指的就是SocketChannel,netty对它进行了
* 封装,称作NioSocketChannel,我们就可以用这个对象的一些方法去读写数据,现在我们想向服务端写数据
* 就调用channel的writeAndFlush方法
*
* 下面是netty与NIO不同的地方:不论是发数据还是接收数据,它们都会走到handler。
* 因此当你调用writeAndFlush方法发送一个“hello world”之后,接下来就走到编码器对应的handler里面了
* 即客户端的24行中的StringEncoder()方法(注意不是addLast方法,这是添加处理器的方法,添加方法在连接
* 建立后就完成了)
* StringEncoder干的事情很简单,就是把“hello world”这个字符串转换为ByteBuf(ByteBuf是Netty中对ByteBuffer的增强)
* 在channel之间传递数据,最终的数据格式都是ByteBuf
*
* 这时候服务端有一个接收READ的另一个EventLoop就会接受到事件。把数据读到ByteBuf后接下来就会走到服务器端的这些处理器上,按照
* 添加的顺序依次对读到的数据进行处理
*/
public class HelloServer {
public static void main(String[] args) {
//ServerBootstrap:服务器端的启动器,专门完成服务器端启动的
//它的功能就是将下面这些Netty提供的各种组件组合到一起作为一个整体的服务进行启动(组装Netty组件)
new ServerBootstrap()
//许多BossEventLoop或WorkerEventLoop
//通过调用group方法,向ServerBootstrap加入了EventLoop组,里面有的EventLoop是BossEventLoop,
//用来处理连接事件,有的EventLoop是WorkerEventLoop,用来处理可读事件
//每一个EventLoop你可以理解为都包含一个线程和一个选择器
.group(new NioEventLoopGroup()) // 1
//选择 服务器的 ServerSocketChannel的实现,Netty在NIO 的基础上又对channel进行了分装
.channel(NioServerSocketChannel.class) // 2
//告诉那些将来作为Worker的EventLoop将来要做哪些事。比如编解码啊、业务处理啊等等
//其中一种操作称为一个handler,有的handler是做编解码的、有的handler是专门对读到的数据
//进行业务处理
.childHandler(
//下面解释:ChannelInitializer
//channel:代表和客户端进行数据读写的通道
//Initializer:初始化器,对于这个channel中有哪些handler做一个初始化
//ChannelInitializer本身也是一个特殊的handler,不过它的职责就是去添加别的handler
//具体是如何添加的呢:即去实现initChannel方法进行添加
new ChannelInitializer<NioSocketChannel>() { // 3
@Override
//initChannel添加两个handler但并不会立即执行初始化器的内容
//只有在accept事件发生后才回去执行下面的方法
protected void initChannel(NioSocketChannel ch) {
//我们添加了如下的两个handler
//对数据进行解码。因为数据传输过来都是字节型的,在Netty中都装在了一个叫ByteBuf的地方
//而StringDecoder就是在将这个ByteBuf转换成字符串
ch.pipeline().addLast(new StringDecoder()); // 5
//自定义handler。打印上一步转换好的字符串
ch.pipeline().addLast(new SimpleChannelInboundHandler() { // 6
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg);
}
});
}
})
.bind(8080); // 4
}
}
接下来是客户端
public class HelloClient {
public static void main(String[] args) throws Exception{
new Bootstrap()
.group(new NioEventLoopGroup()) // 1
.channel(NioSocketChannel.class) // 2
.handler(new ChannelInitializer<Channel>() { // 3
@Override
//在连接建立后才会执行
protected void initChannel(Channel ch) {
ch.pipeline().addLast(new StringEncoder()); // 8
}
})
.connect("127.0.0.1", 8080) // 4
.sync() // 5
.channel() // 6
.writeAndFlush(new Date() + ": hello world!"); // 7
}
}