02- 你的第一款Netty應用程式
阿新 • • 發佈:2021-11-14
本章主要內容:
-
設定開發環境(Java環境、Maven環境)
-
編寫Echo伺服器和客戶端(簡單的請求響應)
-
構建並測試應用
設定開發環境和測試在這裡不多做概述,主要將如何應用Netty構建一個簡單的請求響應客戶端與服務端。
同樣的這裡也不對Netty的各個元件展開說明,後面在每一章節都有詳細的解釋,這一章的目的就是讓你搭建一個簡單的程式,讓它能夠跑起來。
2.2 Netty客戶端/服務端概述
2.3 編寫Echo伺服器
所有的Netty伺服器都需要以下兩部分。
-
至少一個ChannelHandler——該元件實現了伺服器對客戶端接收的資料處理,即它的業務邏輯。
-
引導——這是配置伺服器的啟動程式碼。至少,它會將伺服器繫結到它要監聽連線請求的埠上。
程式碼示例:
EchoServerHandler: @Sharable // 這是一個重點,標識一個ChannelHandler可以被多個Channel安全地共享,後面會展開闡述 public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) { ByteBuf in = (ButeBuf) msg; // 將訊息記錄到控制檯 System.out.println("Server received:" + in.toString(CharsetUtil.UTF-8)); // 將接收到的訊息寫給傳送者,而不沖刷出站訊息 ctx.write(in); } @Override public void channelReadComplete(ChannelHandlerContext ctx) {// 將未決訊息沖刷到遠端節點,並且關閉該Channel ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLLOSE); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 列印異常棧跟蹤、關閉該Channel cause.printStackTrace(); ctx.close(); } }
EchoServer: public class EchoServer { private final int port; public EchoServer(int port) { this.port = port; } // 校驗埠號 public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Usage:" + EchoServer.class.getSimpleName() + "<port>"); return; } // 設定埠值(如果埠值引數的格式不正確,則丟擲一個NumberFormatException) int port = Integer.parseInt(args[0]); // 呼叫伺服器的start()方法 new EchoServer(port).start(); } public void start() throws Exception { final EchoServerHandler serverHandler = new EchoServerHandler(); // 建立EventLoopGroup EventLoopGroup group = new NioEventLoopGroup(); try { // 建立ServerBootstrap ServerBootstrap b = new ServerBootStrap(); b.group(group) .channel(NioServerScoketChannel.class) // 指定所使用的NIO傳輸Channel .localAddress(new InetSocketAddress(port)) // 使用指定的埠設定套接字地址 .childHandler(new ChannelInitializer<SocketChannel>() { // 新增一個EchoServerHandler到子Channel的ChannelPipeline @Override public void initChannel(SocketChannel ch) throws Exception { // EchoServerHandler被標註為@Shareable,所以我們總是可以使用同樣的例項 ch.pipeline().addLast(serverHandler); } }); // 非同步地繫結伺服器,呼叫sync()方法阻塞等待直到繫結完成 ChannelFuture f = b.bind().sync(); // 獲取Channel的CloseFuture,並且阻塞當前執行緒直到它完成 } finally { // 關閉EventLoopGroup,釋放所有資源 group.shutdownGracefully().sync(); } } }
Echo客戶端將會:
-
連線到伺服器
-
傳送一個或多個訊息
-
對於每個訊息,等待並接收從伺服器發回的相同的訊息
-
關閉連線
程式碼示例:
客戶端的ChannelHandler: @Sharable // 標記該類的例項可以被多個Channel共享 public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override public void channelActive(ChannelHandlerContext ctx) { // 當被通知 Channel是活躍的時候,傳送一條訊息 ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!"), CharsetUtil.UTF_8); } @Override public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) { // 記錄已接收訊息的轉儲 System.out.println("Client receive:" + in.toString(CharsetUtil.UTF_8)); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throws cause) { // 發生異常時,記錄錯誤並關閉Channel cause.printStackTrace(); ctx.close(); } }
EchoClient: public class EchoClient { private final String host; private final int port; public EchoClient(String host, int port) { this.port = port; this.host = host; } public void start() throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host, port)) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new EchoClientHandler()); } }); ChannelFuture f = b.connect().sync(); f.channel().closeFuture().sync(); } finally { // 關閉執行緒池並釋放所有資源 group.shutdownGracefully().sync(); } } } public static void main(String[] args) throws Exception { if (args.length != 2) { System.err.println("Usage:" + EchoClient.class.getSimpleName() + "<host><port>"); return; } String host = args[0]; int port = Integet.parseInt(args[1]); new EchoClient(host, port).start(); }
本章小結