Netty上手
1. 目标
开发一个简单的服务器端和客户端:
- 客户端向服务器端发送hello, world
- 服务器仅接收,不返回
2. 工程准备
在idea中创建netty-learn的maven工程, 添加如下依赖:
xml
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.119.Final</version>
</dependency>
3. 服务器端代码实现
java
public class NettyServerDemo1 {
public static void main(String[] args) {
// 1.就是Netty的启动器对象,负责组装 netty各个组件,启动服务器
new ServerBootstrap()
// 2. 所谓EventLoop就是由selector+thread的组成, group表示可以有多个selector+thread
// 用于处理可连接事件,所谓的boss线程
.group(new NioEventLoopGroup())
/** 3. ServerSocketChannel的具体实现选择配置, 具体的实现有如下:
* NioServerSocketChannel是通用的NIO,
* EpollServerSocketChannel,用于linux平台
* KQueueServerSocketChannel 用于mac平台
* OioServerSocketChannel 通用的BIO
*/
.channel(NioServerSocketChannel.class)
// 4. 负责读写操作,所谓childHandler就是worker具体操作
.childHandler(new ChannelInitializer<NioSocketChannel>() {
// 5. 固定格式,在initChannel里面添加具体的bytebuffer处理类
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new StringDecoder());
// 6. 可以添加多个处理类,这里使用自定义的处理类,这里用来打印
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println(msg);
}
});
}
})
// 7. 绑定端口
.bind(8080);
}
}
在位置3处,选择服务Scoket实现类,其中NioServerSocketChannel表示基于NIO的服务器端实现,其它实现还有: 在位置4处,为啥方法叫childHandler,是接下来添加的处理器都是给SocketChannel用的,而不是给 ServerSocketChannel。ChannelInitializer处理器(仅执行一次),它的作用是待客户端SocketChannel建立连接后,执行initChannel以便添加更多的处理器。
4. 客户端代码实现
java
public class NettyClientDemo1 {
public static void main(String[] args) throws InterruptedException {
// 1。 就是Netty的启动器对象,负责组装 netty各个组件,启动客户端
new Bootstrap()
// 2. 所谓EventLoop就是由selector+thread的组成, group表示可以有多个selector+thread
// 用于处理可连接事件,所谓的boss线程
.group(new NioEventLoopGroup())
/** 3. SocketChannel的具体实现选择配置, 具体的实现有如下:
* NioSocketChannel是通用的NIO,
* EpollSocketChannel,用于linux平台
* KQueueSocketChannel 用于mac平台
* OioSocketChannel 通用的BIO
*/
.channel(NioSocketChannel.class)
// 4. 负责读写操作,所谓handler就是worker具体操作
.handler(new ChannelInitializer<NioSocketChannel>() {
// 5. 固定格式,连接后被调用
protected void initChannel(NioSocketChannel ch) {
// 7. 将字符串转成byte
ch.pipeline().addLast(new StringEncoder());
}
})
// 8. 连接服务器
.connect(new InetSocketAddress("localhost", 8080))
// 9. 阻塞方法,直到连接建立
.sync()
// 10. 向服务器发送数据
.channel().writeAndFlush("hello world");
}
}
在位置3处,选择客户SocketChannel实现类,NioSocketChannel表示基于NIO的客户端实现,其它实现还有 在位置9处,Netty中很多方法都是异步的,如connect,这时需要使用
sync()
方法等待connect建立连接完毕。
在位置10处,获取channel对象,它即为通道抽象,可以进行数据读写操作。
💡提示
一开始需要树立正确的观念
- 把channel理解为数据的通道
- 把msg理解为流动的数据,最开始输入是ByteBuf,但经过pipeline的加工,会变成其它类型对象,最后输出又变成ByteBuf
- 把handler理解为数据的处理工序
- 工序有多道,合在一起就是pipeline,pipeline负责发布事件(读、读取完成...)传播给每个handler, handler对自己感兴趣的事件进行处理(重写了相应事件处理方法)
- handler分Inbound和Outbound两类
- 把eventLoop理解为处理数据的工人
- 工人可以管理多个channel的io操作,并且一旦工人负责了某个channel,就要负责到底(绑定)
- 工人既可以执行io操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个channel的待处理任务,任务分为普通任务、定时任务
- 工人按照pipeline顺序,依次按照handler的规划(代码)处理数据,可以为每道工序指定不同的工人