*客戶端
在之前的文章中,客戶端相關的數據讀寫邏輯是通過 Bootstrap
的 handler()
方法指定
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
// 指定連接數據讀寫邏輯
}
});
現在,在 initChannel()
方法裏面給客戶端添加一個邏輯處理器,這個處理器的作用就是負責向服務端寫數據
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new FirstClientHandler());
}
});
ch.pipeline()
返回的是和這條連接相關的邏輯處理鏈,採用了責任鏈模式- 調用
addLast()
方法添加一個邏輯處理器,這個邏輯處理器爲的就是在客戶端建立連接成功之後,向服務端寫數據
*FirstClientHandler源碼:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.nio.charset.Charset;
import java.util.Date;
public class FirstClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) { //客戶端寫數據給服務端
System.out.println(new Date() + ": 客戶端寫出數據");
// 1.獲取數據
ByteBuf buffer = getByteBuf(ctx);
// 2.寫數據
ctx.channel().writeAndFlush(buffer);
}
private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
byte[] bytes = "你好,Netty!".getBytes(Charset.forName("utf-8"));
ByteBuf buffer = ctx.alloc().buffer();
buffer.writeBytes(bytes);
return buffer;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { //讀取服務端的數據
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println(new Date() + ": 客戶端讀到數據 -> " + byteBuf.toString(Charset.forName("utf-8")));
}
}
1、這個邏輯處理器繼承自 ChannelInboundHandlerAdapter
,然後覆蓋了 channelActive()
方法,這個方法會在客戶端連接建立成功之後被調用
2、客戶端連接建立成功之後,調用到 channelActive()
方法,在這個方法裏面,我們編寫向服務端寫數據的邏輯
3、寫數據的邏輯分爲兩步:首先我們需要獲取一個 netty 對二進制數據的抽象 ByteBuf
,上面代碼中, ctx.alloc()
獲取到一個 ByteBuf
的內存管理器,這個內存管理器的作用就是分配一個 ByteBuf
,然後我們把字符串的二進制數據填充到 ByteBuf
,這樣我們就獲取到了 Netty 需要的一個數據格式,最後我們調用 ctx.channel().writeAndFlush()
把數據寫到服務端
*服務端
在之前的文章中,服務端相關的數據處理邏輯是通過 ServerBootstrap
的 childHandler()
方法指定
.childHandler(new ChannelInitializer<NioSocketChannel>() {
protected void initChannel(NioSocketChannel ch) {
// 指定連接數據讀寫邏輯
}
});
現在,在 initChannel()
方法裏面給服務端添加一個邏輯處理器,這個處理器的作用就是負責讀取客戶端來的數據
.childHandler(new ChannelInitializer<NioSocketChannel>() {
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new FirstServerHandler());
}
});
*FirstServerHandler源碼
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.nio.charset.Charset;
import java.util.Date;
public class FirstServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { //讀取來自客戶端的數據
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 = "你好,歡迎學習Netty".getBytes(Charset.forName("utf-8"));
ByteBuf buffer = ctx.alloc().buffer();
buffer.writeBytes(bytes);
return buffer;
}
}
服務端側的邏輯處理器同樣繼承自 ChannelInboundHandlerAdapter
,與客戶端不同的是,這裏覆蓋的方法是 channelRead()
,這個方法在接收到客戶端發來的數據之後被回調。
這裏的 msg
參數指的就是 Netty 裏面數據讀寫的載體,這裏我們強轉之後,然後調用 byteBuf.toString()
就能夠拿到客戶端發過來的字符串數據。
總結:
1、客戶端和服務端的邏輯處理是均是在啓動的時候,通過給邏輯處理鏈 pipeline
添加邏輯處理器,來編寫數據的讀寫邏輯
2、在客戶端連接成功之後會回調到邏輯處理器的 channelActive()
方法,而不管是服務端還是客戶端,收到數據之後都會調用到 channelRead
方法
3、寫數據調用writeAndFlush
方法,客戶端與服務端交互的二進制數據載體爲 ByteBuf
,ByteBuf
通過連接的內存管理器創建,字節數據填充到 ByteBuf
之後才能寫到對端