標籤:版本 apt port 解碼 輔助 oss unp rbo log
這是我剛開始學習netty寫的第一個demo,原準備照著《Netty權威指南》的入門demo敲一邊,等到我去下載jar包搭建環境的時候才發現Netty竟然沒有5.x版本,現在最高 4.1.9。一臉懵逼的我一搜才知道Netty從5.x退回到4.x,具體原因不清楚。4.x與5.x畢竟隔著一個大版本,中間各種異同不是我這種小白現在能搞懂的,還是照著官方的demo來吧。下面這個例子就是參照官網guide中的demo修改而成,大概的邏輯是client 向 server請求server時間並列印。
服務端代碼:
1 public class TimeServerHandler extends ChannelInboundHandlerAdapter{ 2 3 /** 4 * 每個傳入的訊息都要調用 5 * */ 6 @Override 7 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{ 8 ByteBuf buf = (ByteBuf)msg; 9 try{10 byte[] req = new byte[buf.readableBytes()];11 buf.readBytes(req);12 String body = new String(req, "UTF-8");13 System.out.println("伺服器收到的請求:" + body);14 String time = "Query time ?".equals(body) ? new Date().toString() : "fail";15 ByteBuf resp = Unpooled.copiedBuffer(time.getBytes());16 ctx.write(resp);17 }finally{18 buf.release();19 }20 }21 22 //通知ChannelInboundHandlerAdapter 最後一次對channelRead()的調用是當前批量讀取中的最後一條訊息。23 @Override24 public void channelReadComplete(ChannelHandlerContext ctx) {25 /**26 * ctx.write(resp) 是將資訊寫到訊息佇列,而不是每次write就寫入SocketChannel27 * ctx.flush() 是將訊息發送隊列中的資訊寫到SocketChannel中發送給對方,28 * ctx.writeAndFlush(resp) 直接寫入SocketChannel29 * */30 ctx.flush();31 }32 33 //處理過程中出現的異常34 @Override35 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {36 cause.printStackTrace();37 ctx.close();38 }39 40 }
1 public class TimeServer { 2 public void bind(int port) throws Exception{ 3 //NIO線程組,專門用於處理網路事件,本質是Reactor線程組。 4 EventLoopGroup bosser = new NioEventLoopGroup();//用於服務端接受用戶端的串連 5 EventLoopGroup worker = new NioEventLoopGroup();//用於進行SocketChannel的網路讀寫 6 7 try { 8 ServerBootstrap boot = new ServerBootstrap();//啟動NIO服務端的輔助啟動類,降低開發複雜度。 9 10 boot.group(bosser, worker)11 .channel(NioServerSocketChannel.class)12 .option(ChannelOption.SO_BACKLOG, 1024)//配置TCP參數13 .childHandler(new ChannelInitializer<SocketChannel>(){//用於處理網路IO事件(記錄日誌,對訊息進行編解碼)14 @Override15 protected void initChannel(SocketChannel ch) throws Exception {16 ch.pipeline().addLast(new TimeServerHandler());17 }18 });19 20 ChannelFuture cf = boot.bind(port).sync();//非同步地綁定伺服器,調用sync()方法阻塞等待直到綁定完成。21 cf.channel().closeFuture().sync();//擷取ChannelFuture,並且阻塞當前線程直到它完成。22 } finally{23 bosser.shutdownGracefully();//釋放所有資源24 worker.shutdownGracefully();25 }26 27 }28 29 30 public static void main(String[] args) {31 int port = 8080;32 try {33 new TimeServer().bind(port);34 } catch (Exception e) {35 e.printStackTrace();36 }37 }38 }
用戶端代碼:
1 public class TimeClientHandler extends ChannelInboundHandlerAdapter{ 2 3 //在到伺服器的串連已經建立之後將被調用 4 @Override 5 public void channelActive(ChannelHandlerContext ctx){ 6 byte[] req = "Query time ?".getBytes(); 7 ByteBuf firstMessage = Unpooled.buffer(req.length); 8 firstMessage.writeBytes(req); 9 ctx.writeAndFlush(firstMessage);10 }11 12 //當從伺服器接受到一條訊息時被調用13 @Override14 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{15 ByteBuf buf = (ByteBuf)msg;16 byte[] resp = new byte[buf.readableBytes()];17 buf.readBytes(resp);18 String str = new String(resp, "UTF-8");19 System.out.println("伺服器時間 : "+ str);20 }21 22 @Override23 public void channelReadComplete(ChannelHandlerContext ctx) {24 ctx.flush();25 }26 27 //處理過程中出現的異常28 @Override29 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {30 cause.printStackTrace();31 ctx.close();32 }33 34 }
public class TimeClient { public void connect(String host, int port) throws Exception{ EventLoopGroup group = new NioEventLoopGroup(); try{ Bootstrap boot = new Bootstrap(); boot.group(group).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new TimeClientHandler()); } }); ChannelFuture cf = boot.connect(host, port).sync(); cf.channel().closeFuture().sync(); }finally{ group.shutdownGracefully(); } } public static void main(String[] args) { final String host = "127.0.0.1"; final int port = 8080; try { new TimeClient().connect(host, port); } catch (Exception e) { System.out.println("用戶端" + Thread.currentThread().getId() + " 請求出現異常"); } }}
運行代碼:
入門demo完成了,接下來就是在這個demo的基礎上修改,後面的代碼我也會一一記錄。
第一個netty程式