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 <strong>Socket acceptSocket = serverSocket.accept();//注意这里
- </strong>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 <strong>int len = is.read(buffer);//注意这里
- </strong>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 <strong>len = is.read(buffer);//注意这里
- </strong>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 <strong>int len = is.read(buffer);//注意这里
- </strong>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 <strong> len = is.read(buffer);//注意这里
- </strong>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 }
复制代码 效果如图,我们可以通过这两个程序进行聊天:
客户端截图:

服务端截图:

下面就是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的。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |