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
Selector and SelectionKey
Charset
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();
}
}
```