Java网络编程----通过实现简易聊天工具来聊聊BIO

种地  金牌会员 | 2023-5-16 15:05:26 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 962|帖子 962|积分 2886

IO模型即输入输出模型,我们今天主要来聊的是java网络编程中的IO模型---BIO模型。
BIO即阻塞式IO,Blocking IO
blocking [ˈblɒkɪŋ]   
v.        堵塞; 阻塞; 堵住(某人的路等); 挡住(某人的视线等); 妨碍; 阻碍;
那究竟什么是阻塞呢?
这里的阻塞和多线程并发控制中,对未持有锁的线程进行同步阻塞是两个概念。更多的是指停滞不前,由于未接受到指令,只能继续等待的意思。
举个经典的例子:(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )
西餐厅中,有1个服务员负责招待。客人进入餐厅中,服务员会根据客人的需要下单或上菜。
当客人A要求点菜时,服务员A开始下单。在这个过程中,客人中间发生了停顿,或者犹豫不决时,服务员只能阻塞等待,不能直接停滞,忙其他的事情。这就是所谓的阻塞。
这样就会有一个问题,服务员只能做完一件事,再做一件事,当有客人B也有要求时,则不能并发执行,这里就是前文中说的多线程同步。
这里存在两个"阻塞",
1、服务员等待指令,只能原地等候下一个指令。
2、由于服务员数量有限,即使其他客人有指令需要下发,服务员依然无法执行。
blocking 属于前者,即服务员只能等待当前客人继续发送指令。
后者则属于多线程同步问题,由于服务员数量有限,无法并发执行事务。
针对于后者,我们一般通过多线程来解决,而前者才是我们今天聊的重点。
大概明白了什么是blocking阻塞之后,我们来看个由blocking IO实现的聊天工具:
Server端代码:
  1. 1 package com.example.demo.learn.tcp;
  2. 2
  3. 3 import java.io.IOException;
  4. 4 import java.io.InputStream;
  5. 5 import java.io.OutputStream;
  6. 6 import java.net.ServerSocket;
  7. 7 import java.net.Socket;
  8. 8 import java.util.Scanner;
  9. 9
  10. 10 /**
  11. 11  * @discription
  12. 12  */
  13. 13
  14. 14 public class TCPServer {
  15. 15     public static void main(String[] args) throws IOException {
  16. 16         ServerSocket serverSocket = new ServerSocket(9999);
  17. 17         while (true) {
  18. 18             <strong>Socket acceptSocket = serverSocket.accept();//注意这里
  19. </strong>19             ChatThread chatThread = new ChatThread(acceptSocket);
  20. 20             new Thread(chatThread).start();
  21. 21         }
  22. 22     }
  23. 23 }
  24. 24
  25. 25 class ChatThread implements Runnable {
  26. 26     private Socket clientSocket;
  27. 27
  28. 28     ChatThread(Socket clientSocket) {
  29. 29         this.clientSocket = clientSocket;
  30. 30     }
  31. 31
  32. 32     @Override
  33. 33     public void run() {
  34. 34         try {
  35. 35
  36. 36             OutputStream os = clientSocket.getOutputStream();
  37. 37             SayThread sayThread = new SayThread(os);
  38. 38             new Thread(sayThread).start();
  39. 39
  40. 40             InputStream is = clientSocket.getInputStream();
  41. 41             byte[] buffer = new byte[1024];
  42. 42             <strong>int len = is.read(buffer);//注意这里
  43. </strong>43             while (len > 0) {
  44. 44                 String msg = new String(buffer, 0, len);
  45. 45                 System.out.println("");
  46. 46                 System.out.println("receive client msg:");
  47. 47                 System.out.println(msg);
  48. 48                 System.out.println("");
  49. 49                 <strong>len = is.read(buffer);//注意这里
  50. </strong>50             }
  51. 51             clientSocket.close();
  52. 52
  53. 53         } catch (Exception ex) {
  54. 54             //logs
  55. 55         }
  56. 56
  57. 57     }
  58. 58 }
  59. 59
  60. 60 class SayThread implements Runnable {
  61. 61     private OutputStream os;
  62. 62
  63. 63     SayThread(OutputStream outputStream) {
  64. 64         this.os = outputStream;
  65. 65     }
  66. 66
  67. 67     @Override
  68. 68     public void run() {
  69. 69         try {
  70. 70             os.write("server connect success!!!".getBytes());
  71. 71             Scanner inputScanner = new Scanner(System.in);
  72. 72             while (true) {
  73. 73                 String str = inputScanner.nextLine();
  74. 74                 os.write(str.getBytes());
  75. 75                 os.flush();
  76. 76             }
  77. 77
  78. 78         } catch (Exception ex) {
  79. 79             //logs
  80. 80         }
  81. 81
  82. 82     }
  83. 83 }
复制代码
Client端代码:
  1. 1 package com.zzzlei.zxxb.experience;
  2. 2
  3. 3 import java.io.IOException;
  4. 4 import java.io.InputStream;
  5. 5 import java.io.OutputStream;
  6. 6 import java.net.Socket;
  7. 7 import java.util.Scanner;
  8. 8
  9. 9 /**
  10. 10  * @discription
  11. 11  */
  12. 12 public class TCPClient {
  13. 13     public static void main(String[] args) throws IOException {
  14. 14         Socket clientSocket=new Socket("127.0.0.1",9999);
  15. 15         ChatThread chatThread = new ChatThread(clientSocket);
  16. 16         new Thread(chatThread).start();
  17. 17
  18. 18     }
  19. 19 }
  20. 20
  21. 21 class ChatThread implements Runnable {
  22. 22     private Socket clientSocket;
  23. 23
  24. 24     ChatThread(Socket clientSocket) {
  25. 25         this.clientSocket = clientSocket;
  26. 26     }
  27. 27
  28. 28     @Override
  29. 29     public void run() {
  30. 30         try {
  31. 31             OutputStream os = clientSocket.getOutputStream();
  32. 32             SayThread sayThread = new SayThread(os);
  33. 33             new Thread(sayThread).start();
  34. 34
  35. 35             InputStream is = clientSocket.getInputStream();
  36. 36             byte[] buffer = new byte[1024];
  37. 37             <strong>int len = is.read(buffer);//注意这里
  38. </strong>38             while (len > 0) {
  39. 39                 String msg = new String(buffer, 0, len);
  40. 40                 System.out.println("");
  41. 41                 System.out.println("receive server msg :");
  42. 42                 System.out.println(msg);
  43. 43                 System.out.println("");
  44. 44                <strong> len = is.read(buffer);//注意这里
  45. </strong>45             }
  46. 46             clientSocket.close();
  47. 47
  48. 48         } catch (Exception ex) {
  49. 49             //logs
  50. 50         }
  51. 51
  52. 52     }
  53. 53 }
  54. 54
  55. 55 class SayThread implements Runnable {
  56. 56     private OutputStream os;
  57. 57
  58. 58     SayThread(OutputStream outputStream) {
  59. 59         this.os = outputStream;
  60. 60     }
  61. 61
  62. 62     @Override
  63. 63     public void run() {
  64. 64         try {
  65. 65             os.write("client connect success!!!".getBytes());
  66. 66             Scanner inputScanner = new Scanner(System.in);
  67. 67             while (true) {
  68. 68                 String str = inputScanner.nextLine();
  69. 69                 os.write(str.getBytes());
  70. 70                 os.flush();
  71. 71             }
  72. 72
  73. 73         } catch (Exception ex) {
  74. 74             //logs
  75. 75         }
  76. 76
  77. 77     }
  78. 78 }
复制代码
效果如图,我们可以通过这两个程序进行聊天:
客户端截图:

服务端截图:

 
下面就是BIO模型的简示图

服务端在创建好serverSocket之后,会等待客户端socket的连接,当连接成功后,会在服务端和客户端通过Socket进行通信。
在这个程序(模型)中,存在两个阻塞的点:(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )
1、服务器在等待客户端接入,也就是accept的地方。(服务端代码的 18行)
2、服务器或客户端在等待socket写入指令的地方。(服务端代码的42,49行,客户端代码37,44行)
如图,线程虽然是RUNNING状态,但是却不继续执行了:

想一想,这两个地方是不是都是无法通过增加线程来实现?
BIO是Jdk 1.0 时就引入的网络编程模型,Jdk1.4之后,引入了NIO(我会在后文中详细介绍),来解决阻塞问题,让线程不再等待。
那有了NIO是不是就不再需要,BIO了呢?(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )
并不是,BIO的优点是可以通过增加线程进行业务隔离,逻辑清晰,编码和模型实现也都非常简单。
缺点则是如果想提高性能,需要增加多线程支撑,即使如此仍然存在阻塞点导致性能瓶颈上限比较低。
因此在资源满足的情况下,连接数量少时,是比较推荐使用BIO的。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

种地

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表