netty的基础学习
1.是什么?
Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。Netty 是一个广泛使用的 Java 网络编程框架。是一个用以快速开发高性能、高可靠性协议的服务器和客户端。换句话说,Netty 是一个 NIO 客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如服务器和客户端的协议。Netty 大大简化了网络程序的开发过程比如 TCP 和 UDP 的 socket 服务的开发。
2.有什么特点?
-
并发高
Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。NIO中,当一个Socket建立好之后,Thread并不会阻塞去接受这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是不阻塞的,这样就能让一个Thread处理更多的请求了。 -
传输快
Netty的传输快其实也是依赖了NIO的一个特性—零拷贝。
Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。
Netty中,需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。 -
封装好
netty能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能。
netty在应用时,不需要考虑太多问题。比如socket连接的维护、内容传输等。 -
netty的实例
socket连接的维护和内容传输的字节维护
@Override
public void run() {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker); // 绑定线程池
bootstrap.channel(NioServerSocketChannel.class); // 指定使用的channel
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {// 绑定客户端连接时候触发操作
@Override
public void initChannel(SocketChannel ch) throws Exception {
String logStr = DateUtil.getDateTime() + " 客户端 " + ch.remoteAddress()+" 准备连接server";
System.out.println(logStr);
//设置接收到数据的长度
//ch.pipeline().addLast(new FixedLengthFrameDecoder(1500));
//读写空闲。第一个参数表示读操作空闲时间;第二个参数表示写操作空闲时间;第三个参数表示读写操作空闲时间; 第四个参数 单位/秒
//ch.pipeline().addLast("ping", new IdleStateHandler(120, 120, 120, TimeUnit.SECONDS));
//长时间不发消息自行断开
ch.pipeline().addLast(new ReadTimeoutHandler(300));
ch.pipeline().addLast(new ByteToMessageDecoder() { //读取数据的编码设置
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
byte[] bytes = new byte[in.readableBytes()];
in.readBytes(bytes);
out.add(ConversionUtils.bytes_String16(bytes));
}
});
ch.pipeline().addLast(new MessageToByteEncoder<String>(){ //发送数据的编码设置
@Override
protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
byte[] respone = ConversionUtils.hexStrToByteArray(msg);
out.writeBytes(respone);
}
});
ch.pipeline().addLast(new TcpEquHandler()); //客户端实例连接的操作类
}
});
ChannelFuture f = bootstrap.bind(port).sync(); // 服务器异步创建绑定
f.channel().closeFuture().sync(); // 关闭服务器通道
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully(); // 释放线程池资源
worker.shutdownGracefully();
}
}
public class TcpEquHandler extends ChannelInboundHandlerAdapter {
/**
* 通道建立事件
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
}
/**
* channel 通道 action 活跃的
* 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
*/
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* channel 通道 Inactive 不活跃的
* 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
*/
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 接收数据
* @param ctx 连接对象
* @param msg 接收到的消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg){
}
/**
* 功能:读取完毕客户端发送过来的数据之后的操作
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
/**
* 读写空闲触发的事件
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
}