一、客戶端開發時序圖
圖片來源:Netty權威指南(第2版)
二、Netty客戶端開發步驟
使用Netty進行客戶端開發主要有以下幾個步驟:
1、用戶線程創建Bootstrap
Bootstrap b = new Bootstrap();
Bootstrap是Socket客戶端創建工具類,通過API設置創建客戶端相關的參數,異步發起客戶端連接。
2、創建處理客戶端連接、IO讀寫的Reactor線程組NioEventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();
3、通過Bootstrap的ChannelFactory和用戶指定的Channel類型創建用於客戶端連接的NioSocketChannel
b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
此處的NioSocketChannel類似於Java NIO提供的SocketChannel。
4、創建默認的channel Handler pipeline
b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HelloClientHandler()); } });
用於調度和執行網絡事件。
5、異步發起TCP連接
// 發起異步連接操作 ChannelFuture f = b.connect(host, port).sync();
SocketChannel執行connect()操作后有以下三種結果:
- 連接成功,然會true;
- 暫時沒有連接上,服務器端沒有返回ACK應答,連接結果不確定,返回false。此種結果下,需要將NioSocketChannel中的selectionKey設置為OP_CONNECT,監聽連接結果;
- 接連失敗,直接拋出I/O異常
6、由多路復用器在I/O中輪詢個Channel,處理連接結果
7、如果連接成功,設置Future結果,發送連接成功事件,觸發ChannelPipeline執行
8、由ChannelPipeline調度執行系統和用戶的ChannelHandler,執行業務邏輯
三、Netty客戶端開發示例代碼
需求:客戶端端實現,連接服務器端,並向服務器端發送hello Netty。(注:本代碼使用的netty是netty-all-5.0.0.Alpha1-sources.jar版本)
服務器端代碼見Netty學習之服務器端創建
客戶端代碼:
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; public class HelloClient { public void connect(int port, String host) throws Exception { // 配置客戶端NIO線程組 EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HelloClientHandler()); } }); // 發起異步連接操作 ChannelFuture f = b.connect(host, port).sync(); // 等待客戶端鏈路關閉 f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port = 8080; new HelloClient().connect(port, "127.0.0.1"); } }
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; public class HelloClientHandler extends ChannelHandlerAdapter { private final ByteBuf message; public HelloClientHandler() { byte[] req="hello Netty".getBytes(); message=Unpooled.buffer(req.length); message.writeBytes(req); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(message); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
程序運行結果: