目录
1.netty是什么
Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,用于快速开发可维护的高性能协议服务器和客户端。简单来说它将java NIO进行了大量封装,大大降低了java NIO的上手难度。它具有并发高,传输快,封装好的优点。
2.BIO和NIO的区别
BIO:同步阻塞IO,在BIO中每一个连接都需要分配一个线程来执行,线程效率低。
NIO:异步非阻塞IO,引入了selector监听流事件,类似于收发室,不需要每个通信连接由一个单独的线程处理,解决了高并发的性能问题。
3.netty的主要组件及概念
- I/O:各种各样的流(文件、数组、缓冲、管道。。。)的处理(输入输出);
- Channel:通道,代表一个连接,每个Client请对会对应到具体的一个Channel;
- ChannelPipeline:责任链,每个Channel都有且仅有一个ChannelPipeline与之对应,里面是各种各样的Handler;
- handler:用于处理出入站消息及相应的事件,实现我们自己要的业务逻辑;
- EventLoopGroup:I/O线程池,负责处理Channel对应的I/O事件;
- ServerBootstrap:服务器端启动辅助对象;
- Bootstrap:客户端启动辅助对象;
- ChannelInitializer:Channel初始化器;
- ChannelFuture:代表I/O操作的执行结果,通过事件机制,获取执行结果,通过添加监听器,执行我们想要的操作;
- ByteBuf:字节序列,通过ByteBuf操作基础的字节数组和缓冲区。
4.demo
新建一个springboot项目,然后引入netty依赖包
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.72.Final</version>
</dependency>
1.server
/**
* (1)、 初始化用于Acceptor的主"线程池"以及用于I/O工作的从"线程池";
* (2)、 初始化ServerBootstrap实例, 此实例是netty服务端应用开发的入口;
* (3)、 通过ServerBootstrap的group方法,设置(1)中初始化的主从"线程池";
* (4)、 指定通道channel的类型,由于是服务端,故而是NioServerSocketChannel;
* (5)、 设置ServerSocketChannel的处理器
* (6)、 设置子通道也就是SocketChannel的处理器, 其内部是实际业务开发的"主战场"
* (8)、 配置子通道也就是SocketChannel的选项
* (9)、 绑定并侦听某个端口
*/
public class NettyServer {
private final int port;
public NettyServer(int port) {
this.port = port;
}
public void start() throws InterruptedException {
// 服务器端应用程序使用两个NioEventLoopGroup创建两个EventLoop的组,EventLoop这个相当于一个处理线程,是Netty接收请求和处理IO请求的线程。
// 主线程组, 用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 从线程组, 当boss接受连接并注册被接受的连接到worker时,处理被接受连接的流量。
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
try {
// netty服务器启动类的创建, 辅助工具类,用于服务器通道的一系列配置
ServerBootstrap serverBootstrap = new ServerBootstrap();
/**
* 使用了多少线程以及如何将它们映射到创建的通道取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。
* 设置循环线程组,前者用于处理客户端连接事件,后者用于处理网络IO(server使用两个参数这个)
* public ServerBootstrap group(EventLoopGroup group)
* public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)
*/
//传入线程组
serverBootstrap.group(bossGroup, workerGroup);
//指定NIO的模式
serverBootstrap.channel(NioServerSocketChannel.class);
/**
* @Description: 服务端每接收到一个连接请求就会新启一个socket通信,也就是channal,下面代码就是给channal添加handler
*
@Override protected void initChannel(SocketChannel channel) throws Exception {
// 通过SocketChannel去获得对应的管道
ChannelPipeline pipeline = channel.pipeline();
// 通过管道,添加handler
pipeline.addLast("nettyServerOutBoundHandler", new NettyServerOutBoundHandler());
pipeline.addLast("nettyServerHandler", new NettyServerHandler());
}
*/
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//添加handler
socketChannel.pipeline().addLast(new NettyServerHandler());
}
});
//启动server,绑定端口,开始接收进来的连接,设置启动的端口号,同时启动方式为同步
ChannelFuture future = serverBootstrap.bind(port).sync();
//监听关闭的channel,等待服务器 socket 关闭 ,设置位同步方式
future.channel().closeFuture().sync();
} finally {
//关闭线程组
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
}
}
public static void main(String args[]) throws InterruptedException {
int port = 8088;
NettyServer nettyServer = new NettyServer(port);
System.out.println("server start");
nettyServer.start();
System.out.println("server stop");
}
}
2.serverHandler
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* @param [ctx, msg]
* @return void
* @author pwd
* @description 读取客户端发送的消息
* @date 2022/7/13 15:43
**/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("read client msg");
ByteBuf result = (ByteBuf) msg;
byte[] bytesMsg = new byte[result.readableBytes()];
// msg中存储的是ByteBuf类型的数据,把数据读取到byte[]中
result.readBytes(bytesMsg);
String resultStr = new String(bytesMsg);
// 接收并打印客户端的信息
System.out.println("Client said:" + resultStr);
//如果客户端骂人则反弹伤害
if(resultStr.contains("SB")){
// 在当前场景下,发送的数据必须转换成ByteBuf数组
ByteBuf encoded = ctx.alloc().buffer(4 * resultStr.length());
encoded.writeBytes(resultStr.getBytes());
ctx.writeAndFlush(encoded);
}
// 释放资源,这行很关键
result.release();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
/**
* @author pwd
* @description 给客户端发送消息
* @date 2022/7/13 15:47
* @param [ctx]
* @return void
**/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 向客户端发送消息
String response = "hello client!";
// 在当前场景下,发送的数据必须转换成ByteBuf数组
ByteBuf encoded = ctx.alloc().buffer(4 * response.length());
encoded.writeBytes(response.getBytes());
ctx.writeAndFlush(encoded);
}
}
3.client
ublic class NettyClient {
private final int port;
private final String host;
public NettyClient(String host,int port){
this.host = host;
this.port = port;
}
public void start() throws InterruptedException {
//线程组
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 客户端启动类程序
Bootstrap bootstrap = new Bootstrap();
try{
bootstrap.group(workerGroup);
//指定NIO的模式
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
//设置客户端handler
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyClientHandler());
}
});
/** 开启客户端监听,连接到远程节点,阻塞等待直到连接完成*/
ChannelFuture channelFuture = bootstrap.connect(host,port).sync();
/**阻塞等待数据,直到channel关闭(客户端关闭)*/
channelFuture.channel().closeFuture().sync();
}finally {
workerGroup.shutdownGracefully().sync();
}
}
public static void main(String args[]) throws InterruptedException {
String host="127.0.0.1";
int port =8088;
NettyClient nettyClient = new NettyClient(host,port);
System.out.println("client start");
nettyClient.start();
System.out.println("client stop");
}
}
4.clientHandler
public class NettyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* @author pwd
* @description 读取server发送过来的消息
* @date 2022/7/13 15:53
* @param [channelHandlerContext, byteBuf]
* @return void
**/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("server said:"+byteBuf.toString(CharsetUtil.UTF_8));
//客户端很低俗,每次都说脏话,不要学
String msg = "you are SB";
ByteBuf encoded = channelHandlerContext.alloc().buffer(4 * msg.length());
encoded.writeBytes(msg.getBytes());
channelHandlerContext.writeAndFlush(encoded);
Thread.sleep(1000);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//异常退出
cause.printStackTrace();
ctx.close();
}
/**
* @author pwd
* @description 给server发送消息
* @date 2022/7/13 15:54
* @param [ctx]
* @return void
**/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String msg = "hello Server!";
ByteBuf encoded = ctx.alloc().buffer(4 * msg.length());
encoded.writeBytes(msg.getBytes());
ctx.writeAndFlush(encoded);
}
}
5.启动
1.执行server的main方法
2.执行client的main方法
可以在server控制台看到
read client msg
Client said:hello Server!
read client msg
Client said:you are SB
read client msg
Client said:you are SB
可以在client控制台看到
server said:hello client!
server said:you are SB
server said:you are SB
server said:you are SB