NIO:1、Soecket Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。一个Socket由一个IP地址和一个端口号唯一确定。Socket的基本工作过程包含以下四个步骤:1、创建Socket;2、打开连接到Socket的输入输出流;3、按照一定的协议对Socket进行读写操作;4、关闭Socket。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。
Server:ServerSocket serverSocket =new ServerSocket(2013);Socket socket = serverSocket.accept(); //接受到此Socket的连接,在socket连接之前,将阻塞// 由Socket对象得到输入流,并构造相应的BufferedReader对象BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream()));// 获取从客户端读入的字符串String result = bufferedReader.readLine(); //在获取客户端输入之前,又将阻塞//服务端发送给客户端PrintWriter printWriter =new PrintWriter(socket.getOutputStream());printWriter.print("hello Client, I am Server!");printWriter.flush();// 关闭SocketprintWriter.close();bufferedReader.close();socket.close();
Client:// 创建SocketSocket socket =new Socket("127.0.0.1",2013);socket.setSoTimeout(60000); // 60s超时//发给服务端PrintWriter printWriter =new PrintWriter(socket.getOutputStream(),true);printWriter.println("Hi, im cliet.");printWriter.flush();//接收服务端消息,由Socket对象得到输入流BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream()));String result = bufferedReader.readLine(); //若一直未接收到,则阻塞System.out.println("Server say : " + result);
2、IO阻塞
当一个IO读操作发生时,通常经历两个步骤:1,等待数据准备2,将数据从系统内核拷贝到操作进程中JDK1.4提供了对非阻塞IO(NIO)的支持,JDK1.5_update10版本使用epoll替代了传统的select/poll,极大的提升了NIO通信的性能I/O 模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞同步阻塞:在此种方式下,用户进程在发起一个 IO 操作以后,必须等待 IO 操作的完成,只有当真正完成了 IO 操作以后,用户进程才能运行。 JAVA传统的 IO 模型属于此种方式!同步非阻塞:用户进程发起一个 IO 操作以后 边可 返回做其它事情,但是用户进程需要时不时的询问 IO 操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的 CPU 资源浪费。非阻塞:体现在,这个线程可以去干别的,不需要一直在这等着同步:体现在消息通知机制,这个线程仍然要定时的读取stream,判断数据有没有准备好,client采用循环的方式去读取,可以看出CPU大部分被浪费了其中目前 JAVA 的 NIO 就属于同步非阻塞 IO 。异步非阻塞:服务端调用read()方法,若stream中无数据则返回,程序继续向下执行。 当stream中有数据时,操作系统会负责把数据拷贝到用户空间,然后通知这个线程,这里的消息通知机制就是异步! 而不是像NIO那样,自己起一个线程去监控stream里面有没有数据!
IO多路复用 & Reactor模式(反应器模式,事件驱动):https://zhuanlan.zhihu.com/p/27382996https://zhuanlan.zhihu.com/p/27419141
3、NIO三大组件
Chanel/Buffer/Selector
通道和缓冲区,NIO最主要的两个组件。NIO 是基于块 (Block) 的,它以块为基本单位处理数据Buffer 是一块连续的内存块,是 NIO 读写数据的中转地。通道标识缓冲数据的源头或者目的地,它用于向缓冲读取或者写入数据Chanel 是一个双向通道,可读亦可写,对应于传统IO的流Stream,Stream是单向的关系:数据可以从通道读入缓冲区,也可以从缓冲区写入到通道中。int bytesRead = inChannel.read(buf); //read into buffer.int bytesWritten = inChannel.write(buf);//read from buffer into channel.这里的read/write都是channel提供的,故都是以channel角度来看的:read,从channel读数据 => 写入到buf;write,写数据到channel=> 从buf 读数据Buffer:capacity:作为一个内存块,Buffer有一个固定的大小值。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据limit:在读模式下,Buffer的limit表示你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacityposition:写数据到Buffer中时,position表示当前的位置。初始的position值为0.position最大可为capacity – 1. 读取数据时,也是从某个特定位置读,当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。 当将Buffer从写模式切换到读模式,position会被重置为0.
Selector:http://www.molotang.com/articles/906.htmlhttps://segmentfault.com/a/1190000006824196http://ifeve.com/selectors/
1、通过 Selector.open() 打开一个 Selector.2、将 Channel 注册到 Selector 中, 并设置需要监听的事件(interest set)3、不断重复: 调用 select() 方法, 调用 selector.selectedKeys() 获取 selected keys,迭代每个 selected key: 从 selected key 中获取 对应的 Channel ,判断是哪些 IO 事件已经就绪了, 然后处理它们; 将已经处理过的 key 从 selected keys 集合中删除.// 打开服务端 SocketServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打开 SelectorSelector selector = Selector.open();// 服务端 Socket 监听8080端口, 并配置为非阻塞模式serverSocketChannel.socket().bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false);// 将 channel 注册到 selector 中.// 通常先注册一个 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ,注册到 Selector 中。serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//不断轮询while (true) { // 通过调用 select 方法, 阻塞地等待 channel I/O 可操作 if (selector.select(TIMEOUT) == 0) { System.out.print("."); continue; } // 获取 I/O 操作就绪的 SelectionKey, 通过 SelectionKey 可以知道哪些 Channel 的哪类 I/O 操作已经就绪. Iterator keyIterator = selector.selectedKeys().iterator();
}