要了解Netty服務端與用戶端的雙向通信,我們首先要了解服務端與用戶端的啟動過程,然後再了解服務端發送/接收資料的過程以及用戶端發送/接收資料的過程。本文将通過代碼示例的方式讓大家對Netty有一個初步的認識。
服務端啟動過程
流程:
服務端引導類 ServerBootstrap
1.建立兩個NioEventLoopGroup
bossGroup: 監聽端口
workerGroup: 處理每一條連接配接的資料讀寫的線程組
2.配置兩大線程組,通過.group(bossGroup, workerGroup)給引導類配置兩大線程組,即指定線程模型
3.指定服務端的IO模型,通過.channel(NioServerSocketChannel.class)來指定 IO 模型
4.調用childHandler()方法,給引導類建立一個ChannelInitializer,定義連接配接資料讀寫處理邏輯
代碼:
public static void main(String[] args) {
// 建立一個引導類
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 1.建立兩個NioEventLoopGroup
// bossGroup 監聽端口
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
// workerGroup 處理每一條連接配接的資料讀寫的線程組
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
serverBootstrap
// 2.配置兩大線程組
.group(bossGroup, workerGroup)
// 3.指定服務端的IO模型
.channel(NioServerSocketChannel.class)
// 4.調用childHandler()方法,給引導類建立一個ChannelInitializer,定義連接配接資料讀寫處理邏輯
.childHandler(new ChannelInitializer<NioSocketChannel>() {
protected void initChannel(NioSocketChannel ch) {
}
}).bind(8000);
}
總結:要啟動一個Netty服務端,必須要指定三類屬性,分别是線程模型、IO 模型、連接配接讀寫處理邏輯,然後再調用bind(8000),我們就可以在本地綁定一個 8000 端口啟動起來。
用戶端啟動流程
流程:
用戶端啟動的引導類是 Bootstrap
- 與服務端的啟動一樣,我們需要給它指定線程模型,驅動着連接配接的資料讀寫
- 指定 IO 模型為 NioSocketChannel,表示 IO 模型為 NIO
- 給引導類指定一個 handler,這裡主要就是定義連接配接的業務處理邏輯
- 配置完線程模型、IO 模型、業務處理邏輯之後,調用 connect 方法進行連接配接,由于 connect 方法傳回的是一個 Future,是一個異步方法,可以通過 addListener 方法可以監聽到連接配接是否成功,進而列印出連接配接資訊
代碼:
public static void main(String[] args) throws InterruptedException {
// 用戶端引導類
Bootstrap bootstrap = new Bootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap
// 1.指定線程模型
.group(group)
// 2.指定IO類型為NIO
.channel(NioSocketChannel.class)
// IO 處理邏輯
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(new StringEncoder());
}
});
bootstrap.connect("127.0.0.1", 8000).addListener(future -> {
if (future.isSuccess()) {
System.out.println("連接配接成功!");
} else {
System.err.println("連接配接失敗!");
}
});
}
用戶端與服務端雙向通信
服務端讀/寫資料
通過服務端引導類 ServerBootstrap 的childHandler()方法指定服務端相關資料的讀寫邏輯,即 在 initChannel() 方法裡面給服務端添加一個邏輯處理器,負責向用戶端寫資料和讀用戶端的資料
.childHandler(new ChannelInitializer<NioSocketChannel>() {
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new FirstServerHandler());
}
}).bind(8000);
FirstServerHandler
writeAndFlush():寫資料至用戶端 channelRead():從用戶端讀資料
public class FirstServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println(new Date() + ": 服務端讀到資料 -> " + byteBuf.toString(Charset.forName("utf-8")));
// 回複資料到用戶端
System.out.println(new Date() + ": 服務端寫出資料");
ByteBuf out = getByteBuf(ctx);
ctx.channel().writeAndFlush(out);
}
private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
byte[] bytes = "你好,歡迎關注我的微信公衆号,《技術探究猿》!".getBytes(Charset.forName("utf-8"));
ByteBuf buffer = ctx.alloc().buffer();
buffer.writeBytes(bytes);
return buffer;
}
}
用戶端讀/寫資料
通過用戶端引導類 Bootstrap 的 handler() 方法指定用戶端相關的資料讀寫邏輯,即在 initChannel() 方法裡面給用戶端添加一個邏輯處理器,用來負責向服務端寫資料和讀服務端的資料
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new FirstClientHandler());
}
});
FirstClientHandler
writeAndFlush():寫資料至服務端
channelRead():從服務端讀資料
public class FirstClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(new Date() + ": 用戶端寫出資料");
// 1. 擷取資料
ByteBuf buffer = getByteBuf(ctx);
// 2. 寫資料
ctx.channel().writeAndFlush(buffer);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println(new Date() + ": 用戶端讀到資料 -> " + byteBuf.toString(Charset.forName("utf-8")));
}
private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
// 1. 擷取二進制抽象 ByteBuf
ByteBuf buffer = ctx.alloc().buffer();
// 2. 準備資料,指定字元串的字元集為 utf-8
byte[] bytes = "你好,技術猿!".getBytes(Charset.forName("utf-8"));
// 3. 填充資料到 ByteBuf
buffer.writeBytes(bytes);
return buffer;
}
}
寫在最後
一張圖總結一下知識點:
總的來說,Netty的用戶端和服務端的啟動過程還是比較簡單的,感興趣的小夥伴可以跑跑代碼,對Netty有一個初步的印象。接下來将會帶大家了解一下Netty的核心元件Channel、EventLoop、ChannelHandler、EventLoopGroup等