一、InboundHandler 和OutboundHandler的區別
在Netty中,"inbound"表示來自外部來源(如網絡連接)的數據,而"outbound"則表示從應用程序發送到外部目標(如網絡連接或其他服務)的數據。
"Inbound"主要涉及應用程序接收和處理外部數據的過程。這包括從網絡連接讀取數據、解碼處理數據、執行業務邏輯等操作。例如,在一個服務器應用程序中,inbound操作可能涉及監聽和接受客戶端連接,讀取客戶端發送的請求數據,并將其轉發給適當的處理程序進行處理。inbound監聽的事件如下:
- channelRegistered/channelUnregistered
- channelActive/channelInactive
- channelRead
- channelReadComplete
- channelWritabilityChanged
- userEventTriggered
- exceptionCaught
"Outbound"主要涉及應用程序發送數據到外部目標的過程。這包括將數據編碼為網絡傳輸的格式、通過網絡連接發送數據、處理發送的數據等操作。例如,在一個客戶端應用程序中,outbound操作可能涉及將請求數據編碼為適當的傳輸協議,通過網絡連接發送給服務器,并等待響應數據。outbound監聽的事件如下:
- bind
- connect
- disconnect
- close
- deregister
- read
- write
- flush
Netty的Pipeline采用責任鏈設計模式,責任鏈的每個節點是ChannelHandlerContext,通過prev和next節點實現雙向鏈表。ChannelHandlerContext對象封裝了ChannelHandler對象。Pipeline的首尾節點分別是io.netty.channel.DefaultChannelPipeline.HeadContext和io.netty.channel.DefaultChannelPipeline.TailContext。
Pipeline在分發事件的時候,會根據事件類型選擇合適的handler(Inbound/Outbound本身會通用掩碼位注冊監聽的事件類型),可參考?《Netty之ChannelHandlerMask詳解》
總結:
- InboundHandler處理從網絡接收的數據,負責解析和處理輸入數據。
- OutboundHandler處理向網絡發送的數據,負責封裝和處理輸出數據。
- InboundHandler和OutboundHandler在處理數據的方向上有所區別,但它們通常一起使用,通過ChannelPipeline連接在一起,形成一個完整的數據處理鏈。
二、客戶端消息在handler的流向
我們通過簡單的例子來說明
2.1注冊一個簡單的Netty服務器
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class EchoServer {private int port;public EchoServer(int port) {this.port = port;}public void start() throws Exception{EventLoopGroup boss = new NioEventLoopGroup();EventLoopGroup worker = new NioEventLoopGroup();ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(boss, worker).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();for (int i = 0; i < 3; i++) {pipeline.addLast(new InboundHandler(i + 1));}for (int i = 0; i < 3; i++) {pipeline.addLast(new OutboundHandler(i + 1));}System.out.println("handler注冊順序:");pipeline.names().forEach(x -> {System.out.println("handler:" + x);});}}).option(ChannelOption.SO_BACKLOG,100000).childOption(ChannelOption.SO_KEEPALIVE,true);bootstrap.bind(port).sync();System.out.println("服務啟動,監聽端口@"+port);}public static void main(String[] args) throws Exception {new EchoServer(8001).start();}static class InboundHandler extends ChannelInboundHandlerAdapter {private int index;public InboundHandler(int index) {this.index = index;}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("InboundChannel["+index+"]接收消息");ctx.fireChannelRead(msg);if (index == 3) {ctx.channel().write("df");}}}static class OutboundHandler extends ChannelOutboundHandlerAdapter {private int index;public OutboundHandler(int index) {this.index = index;}@Overridepublic void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {System.out.println("OutboundChannel["+index+"]發送消息");ctx.write(msg, promise);}}}
在這個例子,啟動一個簡單的socket服務器,并依次注冊了三個inboundhandler,三個outboundhandler。啟動之后,通過簡單的telnet命令,往socket發送數據
2.2使用telnet發送數據
使用windows自帶的telnet,輸入命令:telnet localhost 8001,然后隨便輸入一個字符
(部分系統需要通過按“Win+R”快捷鍵,打開“運行”對話框,輸入“optionalfeatures”后按回車鍵;在打開的“Windows功能”窗口中,找到并勾選“Telnet客戶端”)
2.3服務端響應
從這個例子,可以看出
InboundHandler是按照Pipleline的加載順序,順序執行;
而OutboundHandler是按照Pipleline的加載順序,逆序執行。
需要注意的是,由于pipeline采取的是責任鏈方式,在任何一個節點,如果沒有把事件往下傳,事件就會在本節點終止。
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("InboundChannel["+index+"]接收消息");ctx.fireChannelRead(msg); //注釋次代碼,則事件不會往下個handler傳遞數據}