本文共 4100 字,大约阅读时间需要 13 分钟。
NIO在操作系统当前一般定义为:non-blocking io,JDK1.4开始新引入了NIO相关的类库,所以有时候人们在Java中有叫他:new io。
当你使用NIO请求时,等待客户端请求,或者等待数据的到达都不会阻塞了,而是直接返回给你一个错误信息。
简单来说就是如果socket标记为阻塞,那么accept调用就会阻塞,直接有连接到达,如果socket标记为非阻塞,并且也没有连接到达,那么就会返回一个EAGAIN或EWOULDBLOCK类型的错误。
服务端代码
import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class SocketNIO { public static void main(String[] args) throws Exception { //保存所有已经连接进来的客户端 ListsocketChannelList = new ArrayList<>(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(9090)); serverSocketChannel.configureBlocking(false);//一定要设置为非阻塞模式 System.out.println(" <服务端启动,并且设置为非阻塞模式!> "); while (true) { SocketChannel accept = serverSocketChannel.accept(); if (accept == null) { Thread.sleep(5000);//这里纯粹为了演示效果,实际情况下可以不需要休眠,直接快速处理后面的read事件 System.out.println("accept 没有阻塞,而是返回了null!"); } else { //如果有请求到达时,设置为后续事件为非阻塞模式 System.out.println("---接受客户端连接请求,并设置为非阻塞模式!---"); accept.configureBlocking(false);//一定要设置为非阻塞模式 socketChannelList.add(accept);//添加集合中 } ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); Iterator iterator = socketChannelList.iterator(); while (iterator.hasNext()) { SocketChannel sc = iterator.next(); //遍历所有客户端,看看是否有数据到达,有则read大于0,没有也不会阻塞了。 try { int read = sc.read(byteBuffer); if (read > 0) { byteBuffer.flip(); byte[] bytes = new byte[byteBuffer.limit()]; byteBuffer.get(bytes); System.out.println("接收到客户端:" + sc.socket().getPort() + ",发送的数据:" + new String(bytes)); byteBuffer.clear(); } else { System.out.println("客户端没有数据到达。。。"); } } catch (Exception e) { System.out.println("---客户端下线---"); byteBuffer.clear(); iterator.remove(); } } } }} 服务端启动,并且设置为非阻塞模式!>
客户端代码
import java.io.*;import java.net.Socket;public class SocketCli { public static void main(String[] args) throws Exception { Socket socket = new Socket("localhost", 9090); System.out.println("客户端启动..."); OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String readLine = reader.readLine(); if (readLine.equals("quit")) { bufferedWriter.write(readLine); bufferedWriter.newLine(); bufferedWriter.flush(); System.exit(-1); } else { bufferedWriter.write(readLine); bufferedWriter.newLine(); bufferedWriter.flush(); } } }}
代码很简单,通过nio提供的api,可以简单的实现一个服务端单线程下不阻塞的处理多个客户端的请求。
BIO模型
NIO模型
两个模型的区别,显而易见,在等待数据到达时BIO是阻塞的,直到数据到达,NIO是通过不断的询问内核,判断是否有数据到达,询问的频率由自己控制,刚才演示的代码中我就设置的每5秒询问一次。
无论是BIO还是NIO,都需要用户主动调用recvfrom函数来询问内核数据是否到达,到达以后再等数据从内核拷贝到用户空间,这种模型就是同步的,只不过BIO是同步阻塞IO,而NIO是同步非阻塞IO。
1、先启动服务端,服务端调用accept函数后,并没有阻塞而是直接返回了EAGAIN,和上面的分析的结果一致。
2、当客户端启动之后,再看服务端调用,accept得到了返回信息,并且开始调用read函数,客户端没有发送任何数据时,read函数也是返回EAGAIN,并没有阻塞。
3、客户端向服务端发送数据。
客户端发送:11111
服务端收到
通过以上的分析,很容易看出以下两点问题:
基于上面的问题,就延伸出了一种的新的模型:多路复用IO模型,当然只有epoll真正解决了这两个问题,之后将另一些一遍文章再分析一下多路复用IO模型。
转载地址:http://iolrb.baihongyu.com/