首先在第一个程序开始学习
Netty是一个基于NIO, 使用Reactor模式实现的网络通信框架.
于是我们通过Reactor模式, 按图索骥, 大概认识一下Netty的主要组件如下:
核心组件:
- 反应器组件: EventLoop - 封装了Selector和Thread, 对事件进行查询
- 处理器: Handler - 多个Handler组成流水线, 以责任链模式完成Channel中事件的处理
- 通道: Channel - 数据传输通道, 同时也是反应器组件的事件源
- 其它组件:
- BootStrap: 使用建造者模式, 创建和配置基于Netty的Server端或者Client端的启动类
- ByteBuf: 缓冲区
代码1: Server端 - 启动类
package netty.discard;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* Instruction:丢弃信息服务器
* Author:@author MaLi
*/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void startServer() {
//创建组装器: 配置Netty的服务器端组件
ServerBootstrap serverBootstrap = new ServerBootstrap();
//1, 创建事件轮询组: 其中的每一个线程 - EventLoop对应一个Reactor或者SunReactor, 这里采用多线程的Reactor模式, 所有设置了两个轮询组
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);// 父轮询组: 用于查询 - 新连接事件
NioEventLoopGroup workerGroup = new NioEventLoopGroup();//子轮询组: 用于查询 - 数据传输事件
serverBootstrap.group(bossGroup, workerGroup); //由组装器设置两个轮询组
//2, 创建通道类型并设置通道参数: 封装NIO中的Channel, 但是比Channel稍有区别(里面内置了PipeLine)
NioServerSocketChannel socketChannel = new NioServerSocketChannel();
//设置通道参数
serverBootstrap.channel(socketChannel.getClass());
serverBootstrap.localAddress(port);
serverBootstrap.option(ChannelOption.SO_KEEPALIVE, true);
//3, 设置Handler事件处理器
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyDiscardHandler());
}
});
try {
//4, 绑定当前Channel到服务器端口 Create a new Channel and bind it.
ChannelFuture channelFuture = serverBootstrap.bind().sync();//这里为什么创建一个Channel, 并绑定到服务器
//关闭通道(为什么要关闭通道, 被关闭的是哪个通道 - ServerSocketChannel父通道, 父通道用于轮询新连接事件)
// Returns the ChannelFuture which will be notified when this channel is closed. - 如果channel被关闭才会调用该回调
// This method always returns the same future instance.
// 5, 设置关闭channel时候的动作? --> 应用停止的时候父Channel才会被关闭, 释放掉底层的文件描述符
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
//6, 关闭两个事件轮询组, 进一步释放资源.
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
DiscardServer server = new DiscardServer(8888);
server.startServer();
}
}
代码2: Server端 - 处理器
package netty.discard;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* Instruction:handler中的生命周期函数
* Author:@author MaLi
*/
public class NettyDiscardHandler extends ChannelInboundHandlerAdapter {
// 如果有新连接被建立
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("NettyDiscardHandler registed");
super.channelRegistered(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("NettyDiscardHandler actived");
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("data read");
super.channelRead(ctx, msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("data read completed");
super.channelReadComplete(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("data reading exception caught");
super.exceptionCaught(ctx, cause);
}
}
代码3: Client端
package netty.discard;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* Instruction:
* Author:@author MaLi
*/
public class DiscardClient {
public static void main(String[] args) {
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress("localhost", 8888)); //连接事件
//socket.getOutputStream().write(1); // 数据发送事件
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}