入门
netty底层采用多路复用技术,是异步的。底层也是采用的NIO。
入门代码
要使用netty,第一步当然是导入坐标
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.39.Final</version>
</dependency>
然后创建服务器端的代码
package com.hs.nettyPrimary;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
/**
1. netty入门,服务器端代码
2. @author hs
3. @date 2021/07/16
*/
public class HelloServer {
public static void main(String[] args) {
// 服务器端的启动器 负责组装netty的组件 并协调它们工作
new ServerBootstrap()
// 添加一个EventLoop组 一个Selector+Thread = EventLoop loop循环 event事件 也就是循环处理事件 group组
.group(new NioEventLoopGroup())
// 选择服务器的ServerSocketChannel实现
.channel(NioServerSocketChannel.class)
// child就相当于之前处理read事件的worker childHandler()方法的作用就是决定了child能干什么事情
// 方法的参数 ChannelInitializer 就代表跟客户端进行读写的通道 它其实也是一个handler 它的作用的加载其他的handler
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
// 添加具体的handler StringDecoder 解码 因为传输都是用的ByteBuffer 这里是将ByteBuffer转换为String
nioSocketChannel.pipeline().addLast(new StringDecoder());
// 自定义handler 处理一些自定义的事情
nioSocketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("客户端接收的参数" + msg);
}
});
}
})
// 端口
.bind(8080);
}
}
服务端的大致流程就是:
- 创建一个ServerBootstrap() 服务器的启动器
- 添加NioEventloopGroup组
- 选择ServerSocketChannel的实现
- 添加child的处理器,这里仅仅是添加,执行会在客户端连接服务器后执行
- 绑定端口
客户端的代码和服务器端很相似
package com.hs.nettyPrimary;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* @author hs
* @date 2021/07/17
*/
public class HelloClient {
public static void main(String[] args) throws InterruptedException {
// 这里也是先创建一个启动器,这是客户端的启动器,注意导包不要导错了
new Bootstrap()
// 这是添加EventLoop的作用主要是体现在服务器端,因为服务器要开多个线程充当boss和worker
.group(new NioEventLoopGroup())
// 选择客户端SocketChannel的实现
.channel(NioSocketChannel.class)
// 添加处理器,下面的方法会在客户端与服务器端连接成功后调用,会调用initChannel()方法
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel socketChannel) throws Exception {
// 服务器那边要接收数据,接收的是字节 就需要使用decoder来编码
// 而客户端这边是发送的字符串,要编码成字节发送,所以这里是使用encoder
socketChannel.pipeline().addLast(new StringEncoder());
}
})
// 连接服务器
.connect("localhost",8080)
// 阻塞方法,直到客户端与服务器端连接建立后才会往下执行
.sync()
// 代表的客户端与服务器之间数据传输的SocketChannel,netty对它做了封装
.channel()
// 发送数据
.writeAndFlush("hello,netty!");
}
}
这时候服务器与客户端都启动后,客户端就会在连接服务器成功后往服务器发送一个"hello,netty!" 的消息,首先会经过客户端的Handler将字符串编码为字节数组进行传输,服务器端刚开始接受的也是字节数组,服务器端的Handler会先将字节数组解码为字符串,然后再由下一个Handler进行处理,也就是我们自定义的输出语句。具体的执行流程如下。
执行流程
-
添加Handel处理器时,仅仅只是添加,还没有执行,需要等到客户端与服务器端连接成功后才会调用initChannel()初始化处理器,等到读写事件发生了就执行相应的处理器
-
事件发生都会先到EventLoop这里,然后在由Handel处理器来进行实际的处理
-
收发数据都会经过处理器
主要组件的理解
-
服务器端msg:可以理解为流动的数据,服务器这边首先接收的是ByteBuf,然后在经过多个pipeline的处理加工,会变成其他类型的对象
-
channel:数据的通道
-
handler理解为处理数据的工序,工序有多个,合在一起就是pipeline(流水线)。pipeline负责发布事件,传播给每个handler,handler再对自己感兴趣的事情进行处理(在自定义handler中重写了相应的事件处理方法)
handler分为两类:Inbound和Outbound 也就是入站和出站
-
EventLoop:处理数据的工人,因为EventLoop有一个线程,真正做事的也就是这个线程
- 工人可以管理多个channel的io操作,并且一个工人如果选择了一个channel就要负责到底(绑定) ,这里主要也只是绑定io操作
- 工人即可以执行io操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个channel待处理的任务,任务可以分为普通任务和定时任务
- 工人按照pipeline顺序,依次按照handler的规则(代码)处理数据,对于非io操作的工序可以为指定不同的工人
总之,meg是数据,channel的传输数据的通道,handler是处理数据的工序,多个工序合在一起就是pipeline,EventLoop是处理数据的工人。