Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。![](https://img-blog.csdnimg.cn/img_convert/3f826eca56155c84c897a74dce9e79b9.png#align=left&display=inline&height=319&margin=[object Object]&originHeight=319&originWidth=672&size=0&status=done&style=none&width=672)
ChannelHandler 接口定义了许多事件处理的方法,通过重写这些方法去实现具体的业务逻辑,API关系如图:
![image.png](https://img-blog.csdnimg.cn/img_convert/c86141573e970f6c6a9d47a90c6f2e96.png#align=left&display=inline&height=377&margin=[object Object]&name=image.png&originHeight=377&originWidth=470&size=83580&status=done&style=none&width=470)
我们需要自定义一个Handler类去继承ChannelInboundHandlerAdapter ,然后通过重写相应的方法实现业务逻辑,需要重写的方法:
…
ChannelPipeline 是一个 Handler 的集合,它负责处理和拦截 inbound 或者 outbound 的事件和操作,相当于一个贯穿 Netty 的链。
![image.png](https://img-blog.csdnimg.cn/img_convert/5c60f6785384d6be8766da18edb754f6.png#align=left&display=inline&height=300&margin=[object Object]&name=image.png&originHeight=300&originWidth=591&size=151448&status=done&style=none&width=591)
ChannelPipeline addFirst ( ChannelHandler… handlers) :把一个业务处理类添加到链的第一个位置
ChannelPipeline addLast ( ChannelHandler… handlers) :把一个业务处理类添加到链的最后一个位置
这是事件处理器上下文对象,Pipeline 链中的实际处理节点,每个节点ChannelHandlerContext 中包含一个具体的事件处理器 ChannelHandler ,同时 ChannelHandlerContext 中也绑定了对应的 Pipeline 和 Channel 的信息,方便对 ChannelHandler 进行调用。常用方法如下:
ChannelFuture close() :关闭通道
ChannelOutboundInvoker flush() :刷新
ChannelFuture writeAndFlush(Object var1) :将数据写到 ChannelPipeline 中,下一个 ChannelHandler 开始处理(出站)
Netty 在创建实例后都需要设置 ChannelOption 参数,ChannelOption 是 Socket 的标准参数,而非 Netty 独创的,常用的配置有:
ChannelOption.SO_BACKLOG :对应 TCP/IP 协议的 listen 函数的 backlog 参数,用来初始化服务器可连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端,多个客户端来的时候,服务端将不能处理的客户端连接放在队列中等待处理,backlog 参数指定了队列的大小
ChannelOption.SO_KEEPALIVE :一直保持连接活动状态
表示 Channel 中异步 I/O 操作的结果,在 Netty 中所有的 I/O 操作都是异步的,I/O 的调用会直接返回,调用者并不能立刻获得结果,但是可以通过 ChannelFuture 来获取 I/O 操作的处理状态。常用方法如下:
Channel channel () :返回当前正在进行 IO 操作的通道
ChannelFuture sync () :等待异步操作执行完毕
EventLoopGroup 是一组 EventLoop 的抽象,Netty 为了更好的利用多核 CPU 资源,一般会有多个EventLoop 同时工作,每一个EventLoop 维护一个 Selector 实例
EventLoopGroup 提供 next 接口,可以从组里面按照一定的规则获取其中一个 EventLoop 来处理任务。在 Netty 服务器编程中,一般需要提供两个 EventLoopGroup ,如 BossEventLoopGroup 和 WorkerEventLoopGroup 。
![image.png](https://img-blog.csdnimg.cn/img_convert/2418094a2c78f1f178cbbf342ed9c710.png#align=left&display=inline&height=287&margin=[object Object]&name=image.png&originHeight=574&originWidth=1110&size=166797&status=done&style=none&width=555)
BossEventLoopGroup 通常是一个单线程的 EventLoop ,EventLoop 维护着一个注册了 ServerSockerChannel 的Selector 实例,BossEventLoop 不断轮询 Selector 将连接事件分离出来,通常是 OP_ACCEPT 事件,然后将接收到的 SocketChannel 交给 WorkerEventLoopGroup ,WorkerEventLoopGroup 会由 next 选择其中一个 EventLoopGroup 来将这个 SocketChannel 注册到 Selector 并对其后续的 IO 事件进行处理。
public NioEventLoopGroup () :构造方法
public Future> shutdownGracefully () :断开连接,关闭
ServerBootstrap 是 Netty 中的服务器端启动类助手,通过它可以完成服务器端的各种配置
Bootstrap 是 Netty 中的客户端启动类助手,通过它可以完成客户端的各种配置
常用方法:
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) :用于服务器端,设置两个 EventLoop
public Bootstrap group(EventLoopGroup parentGroup) :用于客户端,设置一个 EventLoop
public B channel(Class extends C> channelClass) :用来设置一个服务器端的通道实现
public B option(ChannelOption option, T value) :用来给 ServerChannel 添加配置
public ServerBootstrap childOption(ChannelOption childOption, T value) :用来给接收到的通道添加配置
public ServerBootstrap childHandler(ChannelHandler childHandler) :用来设置业务处理类(自定义的 handler)
public ChannelFuture bind(int inetPort) :用于服务器端,设置占用的端口号
public ChannelFuture connect(String inetHost, nt inetPort) : 用于客户端,连接服务器端
Netty 提供的一个专门用来操作缓冲区的工具类
public static ByteBuf copiedBuffer (CharSequence string , Charset charset) :通过给定的数据和字符编码返回一个 ByteBuf 对象(类似 NIO 中的ByteBuffer 对象)
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.ArrayList;
import java.util.List;
/**
* @Desciption 服务端业务处理类
* @Author yucanlian
* @Date 2020-11-05-8:42
*/
public class NettyServerHandler extends SimpleChannelInboundHandler {
public static List<Channel> channels = new ArrayList<>();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
channels.add(incoming);
System.out.println("[Server]:"+incoming.remoteAddress().toString().substring(1)+"在线");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
channels.remove(incoming);
System.out.println("[Server]:"+incoming.remoteAddress().toString().substring(1)+"掉线");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object o) throws Exception {
Channel incoming = ctx.channel();
for (Channel channel : channels){
if (channel != incoming){
channel.writeAndFlush("["+incoming.remoteAddress().toString().substring(1)+"]说:"+o.toString()+"\n");
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
Channel incoming = ctx.channel();
System.out.println("[Server]:"+incoming.remoteAddress().toString().substring(1)+"异常");
ctx.close();
}
}
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* @Desciption
* @Author yucanlian
* @Date 2020-11-05-8:54
*/
public class NettyServer {
private int port;
public NettyServer(int port) {
this.port = port;
}
public void run() throws Exception{
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap server = new ServerBootstrap();
server.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("handler",new NettyServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG,128)
.childOption(ChannelOption.SO_KEEPALIVE,true);
System.out.println("Netty Server start....");
ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();
}finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("Netty Server end....");
}
}
public static void main(String[] args) throws Exception {
new NettyServer(9999).run();
}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
/**
* @Desciption 服务端业务处理类
* @Author yucanlian
* @Date 2020-11-05-8:42
*/
public class NettyClientHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
System.out.println(o.toString().trim());
}
}
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Scanner;
/**
* @Desciption
* @Author yucanlian
* @Date 2020-11-05-9:07
*/
public class NettyClient {
private final String host;
private final int port ;
public NettyClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() {
NioEventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap client = new Bootstrap();
client.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("handler",new NettyClientHandler());
}
});
System.out.println("-------Client is ready-------");
Channel channel = client.connect(host, port).sync().channel();
System.out.println("------"+channel.remoteAddress().toString().substring(1)+"-------");
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
String msg = scanner.nextLine();
channel.writeAndFlush(msg+"\r\n");
}
}catch (Exception e){
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new NettyClient("127.0.0.1",9999).run();
}
}
![image.png](https://img-blog.csdnimg.cn/img_convert/57860b1dacefe196ff3b38e763bea06f.png#align=left&display=inline&height=104&margin=[object Object]&name=image.png&originHeight=207&originWidth=529&size=17258&status=done&style=none&width=264.5)
![image.png](https://img-blog.csdnimg.cn/img_convert/1018fb2a8124cc990b725c02a9f325ef.png#align=left&display=inline&height=119&margin=[object Object]&name=image.png&originHeight=238&originWidth=688&size=22534&status=done&style=none&width=344)
![image.png](https://img-blog.csdnimg.cn/img_convert/5fc35a77679daa6d2be1a7e7c04cd475.png#align=left&display=inline&height=110&margin=[object Object]&name=image.png&originHeight=220&originWidth=651&size=22413&status=done&style=none&width=325.5)