1. Nio入门学习
-
Nio概述
-
JDK 1.4中的
java.nio.*包
中引入新的Java I/O库,其目的是提高速度。实际上,“旧”的I/O包已经使用NIO重新实现过,即使我们不显式的使用NIO编程,也能从中受益。 -
可以认为传统IO是面向字节流的,Nio是面向块的
-
Nio四个核心组件
[外链图片转存失败(img-nhe35745-1564585670321)(C:\Users\xy\AppData\Roaming\Typora\typora-user-images\1562933159797.png)]
-
Buffer
- 存放读写的数据
-
Channel
- 各种各样的channel代表着实体之间的连接,通过它能够执行IO操作
-
Selector and SelectionKey
- 与selectable channel一起组成了多路复用,非阻塞的IO的功能
-
Charset
- Charset与他们相关编解码器(decoder and encoder)一起可以完成字节(byte)与Unicode字符之间的转换
-
-
-
Nio API初试,比较Nio与传统IO之间文件的复制速率
Bio文件复制
public class BioFileCopy implements FileCopy { @Override public void fileCopy(String source, String dest) { BufferedInputStream bufferedInputStream = null; BufferedOutputStream bufferedOutputStream = null; try { bufferedInputStream = new BufferedInputStream(new FileInputStream(source)); bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(dest)); byte[] bytes = new byte[1024]; int len = -1; while ((len = bufferedInputStream.read(bytes)) != -1) { bufferedOutputStream.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); System.err.println("file copy fail!"); } finally { try { if (null != bufferedInputStream) { bufferedInputStream.close(); } if (null != bufferedOutputStream) { bufferedOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
Nio文件复制
public class NioFileCopy implements FileCopy { @Override public void fileCopy(String source, String dest) { FileChannel inChannel = null; FileChannel outChannel = null; try { FileInputStream fileInputStream = new FileInputStream(source); FileOutputStream fileOutputStream = new FileOutputStream(dest); inChannel = fileInputStream.getChannel(); outChannel = fileOutputStream.getChannel(); // allocate the ByteBuffer ByteBuffer byteBuffer = ByteBuffer.allocate(1024); int len = -1; while ((len = inChannel.read(byteBuffer)) != -1) { byteBuffer.flip(); outChannel.write(byteBuffer); byteBuffer.clear(); } } catch (IOException e) { e.printStackTrace(); System.err.println("file copy fail!"); } finally { try { if (null != inChannel) { inChannel.close(); } if (null != outChannel) { outChannel.close(); } } catch (Exception e) { e.printStackTrace(); } } } }
测试代码:
public class FileCopyComparationTest { /** * 210 MB */ private final String sourceFile = "C:\\Users\\xy\\Downloads\\mall-server.zip"; private final String bioDestFile = "C:\\Users\\xy\\Desktop\\bioFileCopy.zip"; private final String nioDestFile = "C:\\Users\\xy\\Desktop\\nioFileCopy.zip"; /** * the bio version */ private final FileCopy bioFileCopy = new BioFileCopy(); /** * the nio version */ private final FileCopy nioFileCopy = new NioFileCopy(); @Test public void test() { long star1 = System.currentTimeMillis(); bioFileCopy.fileCopy(sourceFile,bioDestFile); System.out.println("Bio cost:"+(System.currentTimeMillis()-star1)+"ms!"); long start2 = System.currentTimeMillis(); nioFileCopy.fileCopy(sourceFile,nioDestFile); System.out.println("Nio cost:"+(System.currentTimeMillis()-start2)+"ms!"); } }
-
Buffer缓冲区
-
四大标志位:position,limit,mark,capacity
示例代码:
public class BufferFlagTest { @Test public void test() throws Exception { final int capacity = 1024; // allocate a ByteBuffer ByteBuffer byteBuffer = ByteBuffer.allocate(capacity); System.out.println("before put:"); System.out.println("position:"+byteBuffer.position()); // 0 System.out.println("limit:"+byteBuffer.limit()); // 1024 System.out.println("mark:"+byteBuffer.mark()); System.out.println("capacity:"+byteBuffer.capacity()); // 1024 System.out.println("--------------------------"); String data = "夏齐"; // put the data into the buffer byteBuffer.put(data.getBytes("UTF-8")); System.out.println("after put:"); System.out.println("position:"+byteBuffer.position()); //6 System.out.println("limit:"+byteBuffer.limit()); // 1024 System.out.println("mark:"+byteBuffer.mark()); System.out.println("capacity:"+byteBuffer.capacity()); // 1024 System.out.println("--------------------------"); byteBuffer.flip(); System.out.println("after filp:"); System.out.println("position:"+byteBuffer.position()); // 0 System.out.println("limit:"+byteBuffer.limit()); // 6 System.out.println("mark:"+byteBuffer.mark()); System.out.println("capacity:"+byteBuffer.capacity()); // 1024 System.out.println("--------------------------"); byteBuffer.clear(); System.out.println("after clear:"); System.out.println("position:"+byteBuffer.position()); // 0 System.out.println("limit:"+byteBuffer.limit()); // 1024 System.out.println("mark:"+byteBuffer.mark()); System.out.println("capacity:"+byteBuffer.capacity()); // 1024 } }
-
标志位变化图解
-
Memory Mapped File实现文件复制
public class MemoryMappedFileTest { public static void main(String[] args) { FileChannel inChannel = null; FileChannel outChannel = null; File src = new File("C:\\Users\\xy\\Desktop\\TODO.txt"); String dest = "C:\\Users\\xy\\Desktop\\TODO2.txt"; try { FileInputStream fileInputStream = new FileInputStream(src); // retrieve the channel inChannel = fileInputStream.getChannel(); outChannel = FileChannel.open(Paths.get(dest), StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE,StandardOpenOption.READ); // MMF,memory mapped file MappedByteBuffer inMapBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outMapBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size()); // file copy byte[] bytes = new byte[inMapBuf.capacity()]; inMapBuf.get(bytes); outMapBuf.put(bytes); } catch (IOException e) { e.printStackTrace(); System.err.println("fail!"); } finally { try { if (null != inChannel) { inChannel.close(); } if (null != outChannel) { outChannel.close(); } }catch (IOException e) { e.printStackTrace(); } System.out.println("finish!"); } } }
-
直接(Direct)缓冲区与非直接缓冲区
- 直接缓冲区
[外链图片转存失败(img-ptrAeiEl-1564585670325)(C:\My Desktop File\self learning notes\network programming\directBuffer.png)]
-
非直接缓冲区
[外链图片转存失败(img-ISpyOunw-1564585670326)(C:\My Desktop File\self learning notes\network programming\nonDirectBuffer.png)]
-
区别
直接缓冲区本质上减少了一次将数据从用户地址空间复制到内核地址空间的时间,加快的IO速度,但进行分配比非直接缓冲区要慢。
-
Nio网络通信
[外链图片转存失败(img-2aCKaVzg-1564585670327)(C:\My Desktop File\self learning notes\network programming\nio网络通信.png)]
-
概述
我们通常使用NIO是在网络中使用的,网上大部分讨论NIO都是在网络通信的基础之上的!说NIO是非阻塞的NIO也是网络中体现的!
从上面的图我们可以发现还有一个
Selector
选择器。从一开始我们就说过了,nio的核心要素有:-
Buffer缓冲区
-
Channel通道
-
Selector选择器
我们在网络中使用NIO往往是I/O模型的多路复用模型!
-
Selector选择器就可以比喻成麦当劳的广播。
-
一个线程能够管理多个Channel的状态。
-
-
Nio阻塞通信,即没有Selector
测试代码:
Server:
public class BlockingServer { private static final int port = 45000; private static final ByteBuffer byteBuffer = ByteBuffer.allocate(1024); public static void main(String[] args) throws IOException { // create the ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // bind the ip address and port serverSocketChannel.socket().bind(new InetSocketAddress(port)); System.out.println("Server start at "+new Date()); while (true) { // listening the client connection SocketChannel socketChannel = serverSocketChannel.accept(); System.out.println("establish connection:"+serverSocketChannel); // clear the buffer byteBuffer.clear(); // read the data of client int read = socketChannel.read(byteBuffer); byteBuffer.flip(); // retrieve the client message String cliMsg = null; System.out.println((cliMsg = new String(byteBuffer.array(),0,read,"UTF-8"))); // construct the response message byteBuffer.clear(); byteBuffer.put(("hello,"+cliMsg).getBytes("UTF-8")); // send to the client byteBuffer.flip(); socketChannel.write(byteBuffer); //close the connection socketChannel.close(); break; } serverSocketChannel.close(); System.out.println("Server stop.."); } }
Client:
public class BlockingClient { private static final InetSocketAddress SOCKET_ADDRESS = new InetSocketAddress("127.0.0.1", 45000); private static final ByteBuffer byteBuffer = ByteBuffer.allocate(1024); public static void main(String[] args) throws IOException { // create the SocketChannel SocketChannel socketChannel = SocketChannel.open(); // connect the server socketChannel.socket().connect(SOCKET_ADDRESS); // construct message String msg = "夏齐"; byteBuffer.put(msg.getBytes("UTF-8")); byteBuffer.flip(); // send message socketChannel.write(byteBuffer); byteBuffer.clear(); // read the response message int read = socketChannel.read(byteBuffer); System.out.println(new String(byteBuffer.array(),0,read)); socketChannel.close(); } }
-
非阻塞通信
Server:
public class NonBlockingServer { private static final int port = 45000; private static final ByteBuffer byteBuffer = ByteBuffer.allocate(1024); public static void main(String[] args) throws IOException { // create the ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // bind the ip address and listening port serverSocketChannel.socket().bind(new InetSocketAddress(port)); // config the channel to non-blocking serverSocketChannel.configureBlocking(false); System.out.println("Server start"); while (true) { SocketChannel socketChannel = null; // if no client connects,it will return null,rather than waiting the client to connect while ((socketChannel = serverSocketChannel.accept()) == null) { System.out.println("no client connect,I will sleep 1000 ms!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("I will try retrieve the client again!"); } System.out.println("client:"+socketChannel+" connected!"); // read the message from client int read = socketChannel.read(byteBuffer); byteBuffer.flip(); String cliMsg = new String(byteBuffer.array(),0,read,"UTF-8"); System.out.println("get the client message:"+cliMsg); // business logic try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // response message String response = "Hello,"+cliMsg; byteBuffer.clear(); byteBuffer.put(response.getBytes("UTF-8")); byteBuffer.flip(); socketChannel.write(byteBuffer); socketChannel.close(); break; } serverSocketChannel.close(); } }
Client:
public class NonBlockingClient { public static void main(String[] args) throws IOException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.socket().connect(new InetSocketAddress("127.0.0.1",45000)); socketChannel.configureBlocking(false); while (!socketChannel.finishConnect()) { System.out.println("connection is not established,I will wait 10 ms!"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("I will check the connection again!"); } String msg = "夏齐"; ByteBuffer byteBuffer = ByteBuffer.allocate(1024); byteBuffer.put(msg.getBytes("UTF-8")); byteBuffer.flip(); socketChannel.write(byteBuffer); byteBuffer.clear(); int read = -1; while ((read = socketChannel.read(byteBuffer)) <= 0) { System.out.println("I am not read the server message!"); } System.out.println("server msg:"+new String(byteBuffer.array(),0,read,"UTF-8")); socketChannel.close(); } }
Server with Selector:
public class NonBlockingServerWithSelector { /** * the selector */ private final Selector selector; /** * the ServerSocketChannel */ private final ServerSocketChannel serverSocketChannel; /** * the listening port */ private final int port = 45000; /** * the read write buffer */ private final ByteBuffer byteBuffer = ByteBuffer.allocate(1024); public NonBlockingServerWithSelector() throws IOException { selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); } /** * start the server */ public void start() throws IOException { // config the serverSocketChannel to non-blocking mode serverSocketChannel.configureBlocking(false); // bind the serverSocketChannel id address and port serverSocketChannel.socket().bind(new InetSocketAddress(port)); // register the channel to the selector serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("server start,listening "+port+" port!"); while (true) { // will blocking to wait at least one channel to ready selector.select(); // retrieve all the ready interested events Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); processSelectionKey(selectionKey); iterator.remove(); } } } private void processSelectionKey(SelectionKey selectionKey) throws IOException { if (selectionKey.isValid()) { // process the accept event if (selectionKey.isAcceptable()) { SocketChannel socketChannel = serverSocketChannel.accept(); System.out.println("retrieve the client connection:"+socketChannel); // config the channel to non-blocking mode socketChannel.configureBlocking(false); // register the socketChannel to the selector socketChannel.register(selector,SelectionKey.OP_READ); return; } // process the read event if (selectionKey.isReadable()) { SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); if (processSocketChannel(socketChannel)) { socketChannel.close(); } } } } /** * process communicate with the client * @param socketChannel the specific socketChannel * @return true,if should close the socket channel,otherwise,false */ private boolean processSocketChannel(SocketChannel socketChannel) throws IOException { // read the data into the buffer int read = socketChannel.read(byteBuffer); byteBuffer.flip(); String cliMsg = new String(byteBuffer.array(),0,read,"UTF-8"); System.out.println("retrieve the client message:"+cliMsg); byteBuffer.clear(); // construct the response message String response = "Hello,"+cliMsg; byte[] bytes = response.getBytes("UTF-8"); byteBuffer.put(bytes); byteBuffer.flip(); int write = 0; while ((write = socketChannel.write(byteBuffer)) > 0) { } return true; } public static void main(String[] args) throws IOException { new NonBlockingServerWithSelector().start(); } }
-
-
uct the response message
String response = “Hello,”+cliMsg;
byte[] bytes = response.getBytes(“UTF-8”);
byteBuffer.put(bytes);
byteBuffer.flip();
int write = 0;
while ((write = socketChannel.write(byteBuffer)) > 0) {
}
return true;
}
public static void main(String[] args) throws IOException {
new NonBlockingServerWithSelector().start();
}
}
```