netty,io與nio的區別,先上代碼在分析:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.0.Final</version> </dependency>
依賴引入
io與nio雖然目標不一樣,但操作還是一樣的:
接收請求服務類代碼:
package com.kaige123.daomu.bootjsp.netty; import io.netty.bootstrap.ServerBootstrap; 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.NioServerSocketChannel; public class server { private int port = 8081; public void run() throws Exception { //bossGroup 用來接收進來的連接 EventLoopGroup bossGroup = new NioEventLoopGroup(); //workerGroup 用來處理已經被接收的連接 EventLoopGroup workerGroup = new NioEventLoopGroup(); try { //啓動 NIO 服務的輔助啓動類 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new serverHandle()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); // 服務器綁定端口 ChannelFuture f = b.bind(port).sync(); // 等待服務器 socket 關閉 。 f.channel().closeFuture().sync(); } finally { // 出現異常終止 workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); System.out.println("連接關閉等異常"); } } public static void main(String[] args) throws Exception { new server().run(); } }
服務器綁定端口,綁定收到請求後的處理者,有2個類注意:
bossGroup 與workerGroup ,一個是收到請求池,一個是已連接池。當客戶端斷開後,應該從這裏面剔除
處理請求服務類代碼:
package com.kaige123.daomu.bootjsp.netty; import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.io.*; import java.util.Arrays; public class serverHandle extends ChannelInboundHandlerAdapter { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf result = (ByteBuf) msg; byte[] result1 = new byte[result.readableBytes()]; // msg中存儲的是ByteBuf類型的數據,把數據讀取到byte[]中 result.readBytes(result1); String resultStr = new String(result1); // 釋放資源,這行很關鍵 result.release(); FileOutputStream fileOutputStream = new FileOutputStream("D:\\Java_apiCopy.rar", true); Savedisk(fileOutputStream, result1); sendMsg(ctx); } // 寫入本地磁盤 public void Savedisk(FileOutputStream fileOutputStream, byte[] bytes) throws IOException { fileOutputStream.write(bytes, 0, bytes.length); fileOutputStream.close(); } // 向客戶端發送消息 public void sendMsg(ChannelHandlerContext ctx) { String response = "OK BYTES Client!"; // 發送的數據必須轉換成ByteBuf字節數據數組,進行傳輸 ByteBuf encoded = ctx.alloc().buffer(response.length()); encoded.writeBytes(response.getBytes()); ctx.write(encoded); ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 當出現異常就關閉連接 cause.printStackTrace(); ctx.close(); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } }
msg字節數據,readableBytes一口氣讀取到本次共傳輸的數據
客戶端請求代碼:
package com.kaige123.daomu.bootjsp.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 client { public void connect(String host, int port) throws Exception { EventLoopGroup workerGroup = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(workerGroup); b.channel(NioSocketChannel.class); b.option(ChannelOption.SO_KEEPALIVE, true); b.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new clientHandle()); } }); // 連接發服務器地址 ChannelFuture f = b.connect(host, port).sync(); // 關閉連接 f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); System.out.println("連接關閉等異常"); } } public static void main(String[] args) throws Exception { client client = new client(); client.connect("127.0.0.1", 8081); } }
發送請求,並註冊當連接後,自己的處理者
客戶端處理代碼:
package com.kaige123.daomu.bootjsp.netty; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.io.FileInputStream; public class clientHandle extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("客戶端收到處理"); ByteBuf result = (ByteBuf) msg; byte[] result1 = new byte[result.readableBytes()]; result.readBytes(result1); System.out.println("收到數據:" + result1 + "收到長度: " + result1.length); result.release(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 當出現異常就關閉連接 cause.printStackTrace(); ctx.close(); } // 連接成功後,向server發送消息 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { FileInputStream fileInputStream = new FileInputStream("D:\\Java_api.rar"); byte[] bytes = new byte[1024 * 8]; int len = -1; while ((len = fileInputStream.read(bytes)) != -1) { ByteBuf encoded = ctx.alloc().buffer(len); encoded.writeBytes(bytes, 0, len); ctx.write(encoded); ctx.flush(); } } }
io與nio
io的分析:一個客戶端一個socket連接,獲得in與out流,在建立的相互相互通信。
1:當有n人來時,一人一個服務就吃不住了。(這點不重要)
2:當雙方連接上時,數據並不是要一直交互的,資源就一直耗着,等對方的信息,這段等待時間是無意義的
nio猜測,io有切實幹過n個例題,nio僅是接觸了netty,下面是猜測:
1:netty是一個池,客戶來了養池中,客戶請求抓服務去接待。pass,如果真是這樣,那麼我還不敢用。爲什麼?因爲這樣就不能保證請求者與服務器是同一個,這樣將出現數據混亂的問題
1例子求證:
客戶端分2次發出數據,第一次是給客戶端貼上標籤。第二次請求是看客戶端再次請求時,netty是取出貼在客戶端身上的標籤,還是空標籤。如果是取出標籤,說明處理者與客戶端始終是一個,如果是空,說明對象不是同一個
申請全局變量:String filename;
長度不爲3,是第一次請求去貼上標籤,並將標籤打印出來。
長度爲3,是第二次請求打印全局變量。如果是一對一,那麼這個標籤就會保持。如果抓新的來服務,標籤就是null,查看打印:
開啓2個客戶端去請求,第一次貼標籤,第二次打印標籤。打印的標籤與貼上的標籤一致。說明什麼?說明一開始去服務與過一段時間去服務的都是同一人同一對象。這樣則沒有問題,保證數據的一致性。否則我不敢用
區別在於:
阻塞與非阻塞,當在read或write時,在幹其他的時間可以的。但會出現問題。如上,第一次請求出來,修改10秒。第二次就收不到了,客戶端發完就死掉了。服務器休息完也沒拿到第二次數據。也就是,這個過程,就連讀連寫,過程不能在做其他事情
nio非阻塞,不需要阻塞來接收保證消息是在持續傳輸。即使這個過程中,我去幹其他時間,消息回頭還是會傳遞回來,正常收到。
阻塞非阻塞:
阻塞io:我一直等你來信息,保證消息持續傳輸
非阻塞你:我不用守着你來消息,即使這會我中途去幹其他時間浪費了時間,但最後我返回調用處後這個消息還會被處理得到。
這就是阻塞與非阻塞,其區別在於,傳輸過程中其形式不同,從netty切入到nio