具備非阻塞、信道複用等特性的JAVA NIO極大地提高了TCP通信的效率,由於JAVA NIO具有非阻塞的特性,所以基於這一特性可以使用一個線管理多個鏈接。下面的程序演示了在不使用Selector的情況下簡單模擬了一個TCP服務器的搭建和客戶端的訪問:
1,服務器類Server.java
package com.zws.nio.nosel; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import com.zws.nio.util.NIOHelper; public class Server extends Thread{ private ServerSocketChannel sevChn = null; private SocketChannel chn = null; private boolean block = false; private int port; public Server(int port) { this.port = port; init(); } @Override public void run() { while (!isInterrupted()) { try { //此處的accept方法是一個非阻塞的方法,即不管有沒有客戶端鏈接都會返回,在沒有客戶端鏈接的情況下返回null。 if ((chn = sevChn.accept()) != null) { String msg = NIOHelper.read(chn); System.out.println("Server gets msg:" + msg); msg = "Hello Client..."; NIOHelper.write(chn, msg); chn.close(); } else { //do something else } } catch (IOException e) { e.printStackTrace(); } } } /* * 初始化ServerSocketChannel、SocketChannel */ private void init() { try { sevChn = ServerSocketChannel.open(); sevChn.configureBlocking(block);//配置阻塞,false:非阻塞,true:阻塞 sevChn.socket().bind(new InetSocketAddress(port)); } catch (IOException e) { e.printStackTrace(); } } /** * 關閉服務器 */ public void close() { this.interrupt(); if (sevChn != null) try { sevChn.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Server server = new Server(9003); server.start(); //server.close(); } }
2,客戶端類:Client.java
package com.zws.nio.nosel; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel; import com.zws.nio.util.NIOHelper; public class Client { public static void main(String[] args) { boolean block = false; SocketChannel chn = null; try { chn = SocketChannel.open(); chn.connect(new InetSocketAddress("localhost", 9003)); chn.configureBlocking(block); while (true) { //是否完成鏈接,非阻塞方法。 if (chn.finishConnect()) { String msg = "Hello Server..."; NIOHelper.write(chn, msg); msg = NIOHelper.read(chn); System.out.println("Client gets msg:" + msg); break; } else { //do something else } } } catch (IOException e) { e.printStackTrace(); } finally { if (chn != null) try { chn.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3,輔助類:NIOHelper.java
package com.zws.nio.util; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NIOHelper { public static final String CHARSET_UTF8 = "UTF-8"; public static void write(SocketChannel chn, String msg) throws IOException { byte[] bts = msg.getBytes(CHARSET_UTF8); int len = bts.length; ByteBuffer buffer = ByteBuffer.allocate(len + 4); buffer.put(IntegerUtil.toBytes(len));//前4個字節爲報文長度 buffer.put(bts); buffer.flip(); while (buffer.hasRemaining()) chn.write(buffer); } public static String read(SocketChannel chn) throws IOException { String msg = null; byte[] bts = new byte[4]; ByteBuffer buffer = ByteBuffer.allocate(4); while (chn.read(buffer) == 0) {} buffer.flip(); buffer.get(bts, 0, buffer.remaining()); int size = IntegerUtil.toInt(bts); buffer = ByteBuffer.allocate(size); while (chn.read(buffer) == 0) {} buffer.flip(); bts = new byte[size]; buffer.get(bts, 0, buffer.remaining()); msg = new String(bts,CHARSET_UTF8); return msg; } }
4,輔助類:IntegerUtil.java
package com.zws.nio.util; public class IntegerUtil { /** * 將int轉化成4字節數組 * @param value * @return */ public static byte[] toBytes(int value) { byte[] bytes = new byte[4]; for (int i = 0; i < 4; i++) { bytes[i] = (byte) (value >> 8 * i & 0xFF); } return bytes; } /** * 字節數組轉化int * @param bytes * @return */ public static int toInt(byte[] bytes) { int value = 0; for (int i = 0; i < bytes.length; i++) { byte item = bytes[i]; value += (item & 0xFF) << (8 * i); } return value; } }