這一遍先簡單的講一些netty是如何實現nio的代碼的,後面會詳細講述netty原理
一、Netty是什麼
用一句簡單的話來說就是:Netty 封裝了 JDK 的 NIO,讓你用得更爽,你不用再寫一大堆複雜的代碼了。
用官方正式的話來說就是:Netty 是一個異步事件驅動的網絡應用框架,用於快速開發可維護的高性能服務器和客戶端。
二、爲什麼要使用Netty而不使用NIO
- 使用 JDK 自帶的NIO需要了解太多的概念,編程複雜,一不小心 bug 橫飛
- Netty 底層 IO 模型隨意切換,而這一切只需要做微小的改動,改改參數,Netty可以直接從 NIO 模型變身爲 IO 模型
- Netty 自帶的拆包解包,異常檢測等機制讓你從NIO的繁重細節中脫離出來,讓你只需要關心業務邏輯
- Netty 解決了 JDK 的很多包括空輪詢在內的 Bug
- Netty 底層對線程,selector 做了很多細小的優化,精心設計的 reactor 線程模型做到非常高效的併發處理
- 自帶各種協議棧讓你處理任何一種通用協議都幾乎不用親自動手
- Netty 社區活躍,遇到問題隨時郵件列表或者 issue
- Netty 已經歷各大 RPC框架,消息中間件,分佈式通信中間件線上的廣泛驗證,健壯性無比強大
三、用Netty實現之前的功能
首先添加pom依賴
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
</dependencies>
然後是代碼部分
public class Client {
public static void main(String[] args) {
EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();//客戶端引導類
//EventLoopGroup可以理解爲是一個線程池,這個線程池用來處理連接、接受數據、發送數據
bootstrap.group(nioEventLoopGroup)//多線程處理
.channel(NioSocketChannel.class)//制定通道類型爲NioSocketChannel
.handler(new ChannelInitializer<SocketChannel>() {//業務處理類
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ClientHandler());//註冊handler
}
});
// 4.建立連接
bootstrap.connect("127.0.0.1", 8000).addListener(future -> {
if (future.isSuccess()) {
System.out.println("連接成功!");
} else {
System.err.println("連接失敗!");
}
});
}
}
public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
//客戶端連接服務器後被調用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客戶端連接服務器,開始發送數據......");
byte[] req = "QUERY TIME ORDER".getBytes();//請求消息
ByteBuf firstMessage = Unpooled.buffer(req.length);//發送類
firstMessage.writeBytes(req);//發送
ctx.writeAndFlush(firstMessage);//flush
}
//從服務器收到數據後調用
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
System.out.println("client 讀取server數據..");
//服務器返回消息後
byte[] req = new byte[buf.readableBytes()];//創建一個存儲信息的byte數組
buf.readBytes(req);//將buffer中的數據讀到byte數組中
String body = new String(req, "UTF-8");//將byte數組轉換爲String(並轉碼)
System.out.println("服務端數據爲:" + body);//打印服務端反饋的信息
}
//發生異常時調用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("client exceptionCaught...");
//釋放資源
ctx.close();
}
}
public class Server {
public static void main(String[] args) {
// bossGroup表示監聽端口,accept 新連接的線程組,workerGroup表示處理每一條連接的數據讀寫的線程組
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
//server端引導類,來引導綁定和啓動服務器;
ServerBootstrap serverBootstrap = new ServerBootstrap();
//裝配ServerBootstrap
serverBootstrap.group(bossGroup, workerGroup)//多線程處理
//制定通道類型爲NioServerSocketChannel,一種異步模式的可以監聽新進來的TCP連接的通道
.channel(NioServerSocketChannel.class)
//設置childHandler執行所有的連接請求
//註冊handler
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ServerHandler());
}
});
serverBootstrap.bind(8000);
}
}
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server 讀取數據......");
//讀取數據
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("接收客戶端數據:" + body);
//向客戶端寫數據
System.out.println("server向client發送數據");
String currentTime = new Date(System.currentTimeMillis()).toString();
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("server 讀取數據完畢...");
ctx.flush();//刷新後纔將數據發出到SocketChannel
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
可以先嚐試一下,後面會詳細講netty的原理