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

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 package com.example.demo.learn.tcp;  2   3 import java.io.IOException;  4 import java.io.InputStream;  5 import java.io.OutputStream;  6 import java.net.ServerSocket;  7 import java.net.Socket;  8 import java.util.Scanner;  9  10 /** 11  * @discription 12  */ 13  14 public class TCPServer { 15     public static void main(String[] args) throws IOException { 16         ServerSocket serverSocket = new ServerSocket(9999); 17         while (true) { 18             Socket acceptSocket = serverSocket.accept();//注意这里 19             ChatThread chatThread = new ChatThread(acceptSocket); 20             new Thread(chatThread).start(); 21         } 22     } 23 } 24  25 class ChatThread implements Runnable { 26     private Socket clientSocket; 27  28     ChatThread(Socket clientSocket) { 29         this.clientSocket = clientSocket; 30     } 31  32     @Override 33     public void run() { 34         try { 35  36             OutputStream os = clientSocket.getOutputStream(); 37             SayThread sayThread = new SayThread(os); 38             new Thread(sayThread).start(); 39  40             InputStream is = clientSocket.getInputStream(); 41             byte[] buffer = new byte[1024]; 42             int len = is.read(buffer);//注意这里 43             while (len > 0) { 44                 String msg = new String(buffer, 0, len); 45                 System.out.println(""); 46                 System.out.println("receive client msg:"); 47                 System.out.println(msg); 48                 System.out.println(""); 49                 len = is.read(buffer);//注意这里 50             } 51             clientSocket.close(); 52  53         } catch (Exception ex) { 54             //logs 55         } 56  57     } 58 } 59  60 class SayThread implements Runnable { 61     private OutputStream os; 62  63     SayThread(OutputStream outputStream) { 64         this.os = outputStream; 65     } 66  67     @Override 68     public void run() { 69         try { 70             os.write("server connect success!!!".getBytes()); 71             Scanner inputScanner = new Scanner(System.in); 72             while (true) { 73                 String str = inputScanner.nextLine(); 74                 os.write(str.getBytes()); 75                 os.flush(); 76             } 77  78         } catch (Exception ex) { 79             //logs 80         } 81  82     } 83 }

Client端代码:

 1 package com.zzzlei.zxxb.experience;  2   3 import java.io.IOException;  4 import java.io.InputStream;  5 import java.io.OutputStream;  6 import java.net.Socket;  7 import java.util.Scanner;  8   9 /** 10  * @discription 11  */ 12 public class TCPClient { 13     public static void main(String[] args) throws IOException { 14         Socket clientSocket=new Socket("127.0.0.1",9999); 15         ChatThread chatThread = new ChatThread(clientSocket); 16         new Thread(chatThread).start(); 17  18     } 19 } 20  21 class ChatThread implements Runnable { 22     private Socket clientSocket; 23  24     ChatThread(Socket clientSocket) { 25         this.clientSocket = clientSocket; 26     } 27  28     @Override 29     public void run() { 30         try { 31             OutputStream os = clientSocket.getOutputStream(); 32             SayThread sayThread = new SayThread(os); 33             new Thread(sayThread).start(); 34  35             InputStream is = clientSocket.getInputStream(); 36             byte[] buffer = new byte[1024]; 37             int len = is.read(buffer);//注意这里 38             while (len > 0) { 39                 String msg = new String(buffer, 0, len); 40                 System.out.println(""); 41                 System.out.println("receive server msg :"); 42                 System.out.println(msg); 43                 System.out.println(""); 44                 len = is.read(buffer);//注意这里 45             } 46             clientSocket.close(); 47  48         } catch (Exception ex) { 49             //logs 50         } 51  52     } 53 } 54  55 class SayThread implements Runnable { 56     private OutputStream os; 57  58     SayThread(OutputStream outputStream) { 59         this.os = outputStream; 60     } 61  62     @Override 63     public void run() { 64         try { 65             os.write("client connect success!!!".getBytes()); 66             Scanner inputScanner = new Scanner(System.in); 67             while (true) { 68                 String str = inputScanner.nextLine(); 69                 os.write(str.getBytes()); 70                 os.flush(); 71             } 72  73         } catch (Exception ex) { 74             //logs 75         } 76  77     } 78 }

效果如图,我们可以通过这两个程序进行聊天:

客户端截图:

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

服务端截图:

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

 

下面就是BIO模型的简示图

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

服务端在创建好serverSocket之后,会等待客户端socket的连接,当连接成功后,会在服务端和客户端通过Socket进行通信。

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

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

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

 

发表评论

评论已关闭。

相关文章