• 通俗易懂的JAVA BIO NIO AIO 原理白话文解释,区别,优缺点及代码使用案例


    BIO 介绍与原理

    BIO 全程Blocking I/O ,阻塞io, 是java最早的 网络io模型
    基于基本ServerSocket ,Socket 实现,
    SOCKET 所有的操作都是基于阻塞IO的,比如socket inpustream read方法
    如果没有数据进来就会一直阻塞,除非新创建线程处理每一个socket
    

    BIO 匹配我们饭店吃饭场景

    我们先到第一个窗口,发现厨师还没做好,那么就一直等着,直到这到菜做好了,我们拿到这个菜,然会回到座位吃完了
    然后又去第二个窗口,等待第二道菜做好,然后拿回座位吃完,以此循环下去
    以上场景可以匹配ServerSocket 的accept 方法,以及Socket 的read write方法
    一般解决方案是每一个socket 启用一个thread 处理,这会导致并发数很低
    

    BIO 优缺点:

    优点:代码通俗易懂,在低并发场景或者分布式场景依然应用很广
    缺点: 所有流程都是阻塞模式,需要依赖创建多个thread 处理每个socket,并发量很低
    

    BIO运行图:

     

    BIO 案例代码:

    服务端最简单示例:

    创建服务端有客户端连接后发送hello 字符串,并接受客户端reply 回复

    1. /**
    2. * @description: demos of jdk8 java.net.ServerSocket class
    3. * 创建服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复
    4. *
    5. * */
    6. @Test
    7. public void testServer() throws IOException {
    8. ServerSocket serverSocket = new ServerSocket(8679);
    9. while(true){
    10. Socket socket = serverSocket.accept();
    11. socket.getOutputStream().write("hello".getBytes());
    12. try{
    13. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    14. byte[] bytes = new byte[1024];
    15. int read = 0;
    16. while ((read =socket.getInputStream().read(bytes))>0){
    17. byteBuffer.put(bytes,0,read);
    18. byteBuffer.flip();
    19. String rs = new String(byteBuffer.array());
    20. System.out.println(rs);
    21. byteBuffer.clear();
    22. }
    23. }catch (Exception e){
    24. e.printStackTrace();
    25. }finally {
    26. if(!socket.isClosed()) {
    27. try {
    28. socket.close();
    29. }catch (Exception e1){
    30. e1.printStackTrace();
    31. }
    32. }
    33. }
    34. }
    35. }

    客户端最简单示例:
    创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复 
    
    1. /**
    2. * @description: demos of jdk8 java.net.Socket class
    3. * 创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复
    4. */
    5. @Test
    6. public void testClient() throws IOException {
    7. Socket socket = new Socket("127.0.0.1", 8679);
    8. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    9. byte[] bytes = new byte[1024];
    10. int read = 0;
    11. while ((read = socket.getInputStream().read(bytes)) > 0) {
    12. byteBuffer.put(bytes, 0, read);
    13. byteBuffer.flip();
    14. String rs = new String(byteBuffer.array());
    15. byteBuffer.clear();
    16. System.out.println(rs);
    17. socket.getOutputStream().write("reply".getBytes());
    18. }
    19. }

    NIO 介绍与原理

     NIO 全称 non-blocking I/O ,意思是非阻塞io
     与上面BIO 对应的,是JAVA 后加入的nio 包内的网络io模型
     核心类是Selector ,    ServerSocketChannel,SocketChannel
    

    NIO 匹配我们饭店吃饭场景

    同样是进了一个饭店,饭店有几个窗口,每个窗口做不同的菜,比如宫保鸡丁,油闷大虾,醋溜鱼等等
    我们这次选择另一种方式等菜,我们不断的在每个窗口走来走去,而不是一直等在某个窗口
    每一轮查看就把所有做好的菜一起打包带走,拿回来放到桌上吃完,然后在去窗口再继续一轮轮循环往复
    这样做对程序来说的好处就是一直只有一个线程在不断循环监听某些事件就绪了也就是Selector的select()方法,相当于上面的查看哪些菜好了
    

    NIO 优缺点:

    优点:这样做极大的压缩了需要使用的线程,极大的提高了并发量
    确定:在高并发场景下会有阻塞问题,因为在处理某个任务的时候其他事件的处理需要等待

    NIO运行图:

    NIO 案例代码:

    NIO 服务端最简单示例: 

    创建服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复

    1. /**
    2. * @description: demos of jdk8 java.nio.ServerSocketChannel class
    3. * java.nio.ServerSocketChannel 作用:ServerSocketChannel 是 socket nio server 服务端
    4. * ServerSocketChannel 例子:
    5. * nio 服务端简单demo,流程如下
    6. * 1. 服务端创建并注册OP_ACCEPT事件到selector 中
    7. * 2. 循环获取selector中事件
    8. * 2.1 如果是OP_ACCEPT 事件,则获取到对应连接的客户端socketChannel
    9. * 2.2 将socketChannel 注册读事件OP_READ到selector 中
    10. * 2.3 如果是OP_READ ,则读取信息
    11. */
    12. @Test
    13. public void testServerSocketChannel() throws IOException {
    14. Selector selector = Selector.open();
    15. ServerSocketChannel ssc = ServerSocketChannel.open();
    16. ssc.bind(new InetSocketAddress(8970));
    17. ssc.configureBlocking(false);
    18. ssc.register(selector, SelectionKey.OP_ACCEPT);
    19. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    20. while (true) {
    21. selector.select();
    22. Iterator skIterator = selector.selectedKeys().iterator();
    23. while (skIterator.hasNext()) {
    24. SelectionKey sk = skIterator.next();
    25. try {
    26. if (sk.isValid() && sk.isAcceptable()) {
    27. ServerSocketChannel assc = (ServerSocketChannel) sk.channel();
    28. SocketChannel asc = assc.accept();
    29. asc.configureBlocking(false);
    30. asc.register(selector, SelectionKey.OP_READ);
    31. byteBuffer.clear();
    32. byteBuffer.put("hello".getBytes());
    33. byteBuffer.flip();
    34. asc.write(byteBuffer);
    35. }
    36. if (sk.isValid() && sk.isReadable()) {
    37. SocketChannel sc = (SocketChannel) sk.channel();
    38. byteBuffer.clear();
    39. sc.read(byteBuffer);
    40. byteBuffer.flip();
    41. String rs = new String(byteBuffer.array());
    42. System.out.println(rs);
    43. }
    44. skIterator.remove();
    45. } catch (Exception e) {
    46. //e.printStackTrace();
    47. sk.cancel();
    48. sk.channel().close();
    49. }
    50. }
    51. }
    52. }

    客户端最简单示例:

    创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复

    1. /**
    2. * @description: demos of jdk8 java.nio.ServerChannel class
    3. * java.nio.ServerChannel 作用:ServerChannel 是 客户端
    4. * ServerChannel 例子:
    5. */
    6. @Test
    7. public void testServerChannel() throws IOException {
    8. SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8970));
    9. sc.configureBlocking(false);
    10. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    11. while (true) {
    12. byteBuffer.clear();
    13. while (sc.read(byteBuffer) > 0) {
    14. byteBuffer.flip();
    15. String rs = new String(byteBuffer.array());
    16. System.out.println(rs);
    17. byteBuffer.clear();
    18. byteBuffer.put("reply".getBytes());
    19. byteBuffer.flip();
    20. sc.write(byteBuffer);
    21. }
    22. }
    23. }

    AIO 介绍与原理

    AIO 全称 Asynchronous I/O ,意思是异步io,属于完全的非阻塞io
    也是NIO 包内的异步网络io模型 ,与上面NIO 相比,去掉了循环监听事件并收集取回遍历的过程
    核心类是 AsynchronousServerSocketChannel AsynchronousSocketChannel CompletionHandler
    

    AIO 匹配我们饭店吃饭场景

    我也又进入了这家饭店,饭店有几个窗口,每个窗口做不同的菜,比如宫保鸡丁,油闷大虾,醋溜鱼等等
    这次我们换了一种更方便的方式,我们不在每个窗口前走来走去看哪个菜好了
    而是告诉每个窗口放菜的人,我在某某号桌,菜好了直接送到我桌子上就行,这样就完全避免了任何阻塞
    无论在任何场景 accept ,read ,write 等都是注册一个回调函数,然后这些事件发生了自然就会回调到这些函数
    

    AIO 优缺点:

    优点:完全异步io,没有阻塞问题, 最大限度的提高并发量
    缺点:需要计算机操作系统底层支持事件注册回调函数,目前来说大多数linux系统都支持注册回调,windows以及其他一些系统部分不支持,不支持的时候默认会用nio实现

    AIO运行图:

    AIO 案例代码:

    服务端最简单示例:

    创建AIO 服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复

    1. /**
    2. * @description: demos of jdk8 java.nio.AsynchronousServerSocketChannel CompletionHandler class
    3. * 创建AIO 服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复
    4. */
    5. @Test
    6. public void testAIOServer() throws IOException {
    7. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    8. AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8097));
    9. serverSocketChannel.accept(null, new CompletionHandler() {
    10. @Override
    11. public void completed(AsynchronousSocketChannel asc, Object attachment) {
    12. byteBuffer.clear();
    13. byteBuffer.put("hello".getBytes());
    14. byteBuffer.flip();
    15. asc.write(byteBuffer, null, new CompletionHandler() {
    16. @Override
    17. public void completed(Integer result, Object attachment) {
    18. byteBuffer.clear();
    19. }
    20. @Override
    21. public void failed(Throwable exc, Object attachment) {
    22. }
    23. });
    24. asc.read(byteBuffer, null, new CompletionHandler() {
    25. @Override
    26. public void completed(Integer result, Object attachment) {
    27. byteBuffer.flip();
    28. String rs = new String(byteBuffer.array());
    29. System.out.println(rs);
    30. }
    31. @Override
    32. public void failed(Throwable exc, Object attachment) {
    33. }
    34. });
    35. }
    36. @Override
    37. public void failed(Throwable exc, Object attachment) {
    38. }
    39. });
    40. while(true) {}
    41. }

     客户端最简单示例:

    创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复

    1. /**
    2. * @description: demos of jdk8 java.nio.AsynchronousSocketChannel CompletionHandler class
    3. * 创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复
    4. */
    5. @Test
    6. public void testAIOClient() throws IOException {
    7. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    8. AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open();
    9. asynchronousSocketChannel.connect(new InetSocketAddress("127.0.0.1", 8097), null, new CompletionHandler() {
    10. @Override
    11. public void completed(Void result, Object attachment) {
    12. byteBuffer.clear();
    13. asynchronousSocketChannel.read(byteBuffer, null, new CompletionHandler() {
    14. @Override
    15. public void completed(Integer result, Object attachment) {
    16. byteBuffer.flip();
    17. String rs = new String(byteBuffer.array());
    18. System.out.println(rs);
    19. byteBuffer.clear();
    20. byteBuffer.put("relpy".getBytes());
    21. byteBuffer.flip();
    22. asynchronousSocketChannel.write(byteBuffer);
    23. }
    24. @Override
    25. public void failed(Throwable exc, Object attachment) {
    26. }
    27. });
    28. }
    29. @Override
    30. public void failed(Throwable exc, Object attachment) {
    31. }
    32. });
    33. while(true) {}
    34. }

  • 相关阅读:
    Ubuntu安装samba服务器
    华为云API对话机器人CBS的魅力—实现简单的对话操作
    霸榜Github三个月的「架构师成长手册」成为架构师也有捷径
    HarmonyOS4.0系统性深入开发34栅格布局(GridRow/GridCol)
    为什么要重置css样式,如何重置css样式?使用reset.css重置样式,还是使用normalize.css重置样式,他们的区别是什么?
    笔试强训(三十三)
    Spark_SQL-DataFrame数据写出以及读写数据库(以MySQl为例)
    IntelliJ IDEA 2023.2更新了
    alginate-Ferrocene|海藻酸钠-二茂铁|二茂铁修饰改性海藻酸钠|海藻酸钠-peg-二茂铁
    C++ 语言学习 day14 复习 (6)
  • 原文地址:https://blog.csdn.net/madness1010/article/details/127576618