IO 与 NIO之网络通信
一、阻塞IO / 非阻塞NIO
阻塞IO:当一条线程执行 read() 或者 write() 方法时,这条线程会一直阻塞直到读取到了一些数据或者要写出去的数据已经全部写出,在这期间这条线程不能做任何其他的事情。
非阻塞NIO:NIO 与原有的 IO 有同样的作用和目的,但是使用的方式完全不同,NIO 支持面向缓冲区的、基于通道的操作。NIO 将以更加高效的方式进行文件读写操作。JAVA NIO的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。对数据进行处理。
二、传统IO测试代码如下
当出现 accept() 、read() 等方法是就会阻塞。
1 /** 2 * 传统socket服务端 3 * @author -zhengzx- 4 */ 5 public class OioServer { 6 7 @SuppressWarnings("resource") 8 public static void main(String[] args) throws Exception { 9 10 //创建socket服务,监听10101端口 11 ServerSocket server=new ServerSocket(10101); 12 System.out.println("服务器启动!"); 13 while(true){ 14 //获取一个套接字(阻塞) 15 final Socket socket = server.accept();//(测试时可以通过:telnet 127.0.0.1 10101。进行测试) 16 System.out.println("来个一个新客户端!"); 17 //业务处理 18 handler(socket); 19 } 20 } 21 22 /** 23 * 读取数据 24 * @param socket 25 * @throws Exception 26 */ 27 public static void handler(Socket socket){ 28 try { 29 byte[] bytes = new byte[1024]; 30 InputStream inputStream = socket.getInputStream(); 31 32 while(true){ 33 //读取数据(阻塞) 34 int read = inputStream.read(bytes); 35 if(read != -1){ 36 System.out.println(new String(bytes, 0, read)); 37 }else{ 38 break; 39 } 40 } 41 } catch (Exception e) { 42 e.printStackTrace(); 43 }finally{ 44 try { 45 System.out.println("socket关闭"); 46 socket.close(); 47 } catch (IOException e) { 48 e.printStackTrace(); 49 } 50 } 51 } 52 }
三、阻塞 IO解决办法
可以通过线程池创建多线程,为每一次连接创建一个新的线程来执行。问题是对于长连接而言,线程过多时会严重消耗系统资源导致性能下降。比较适合短连接的应用。
四、NIO 的非阻塞模式
Java NIO 有阻塞模式和非阻塞模式,阻塞模式的 NIO除了使用 Buffer存储数据外和 IO基本没有区别,允许一条线程从 Channel 中读取数据,通过返回值来判断 buffer中是否有数据,如果没有数据,NIO不会阻塞,因为不阻塞这条线程就可以去做其他的事情,过一段时间再回来判断一下有没有数据。
*Selectors:Java NIO的 selectors 允许一条线程去监控多个 channels的输入,你可以向一个 selector上注册多个 channel,然后调用 selector 的select() 方法判断是否有新的连接进来或者已经在 selector 上注册时 channel 是否有数据进入。selector 的机制让一个线程管理多个 channel 变得简单。
五、NIO示例代码如下
客户端使用 SocketChannel,服务端使用 ServerSocketChannel 获取通道
六 、selector.select()
selector.select() 虽阻塞,但可以通过 selector.wakeup()唤醒 selector 执行,也可以通过 selector.select(int timeout) 设置时间限制,timeout 时间后唤醒 selector。
七、NIO提高性能
添加多线程,一个线程对应一个 selector,端口的监听可以单独创建一个 selector。(既Netty的工作原理)
总结:NIO允许你用一个单独的线程或几个线程管理很多个 channels(网络的或者文件的),代价是程序的处理和处理 IO相比更加复杂。如果你需要同时管理成千上万的连接,但是每个连接只发送少量数据,例如一个聊天服务器,用 NIO实现会更好一些,相似的,如果你需要保持很多个到其他电脑的连接,例如P2P网络,用一个单独的线程来管理所有出口连接是比较合适的。
IO:如果你只有少量的连接但是每个连接都占有很高的带宽,同时发送很多数据,传统的 IO会更适合