BIO 同步阻塞模型

简介: BIO 同步阻塞模型

Java BIO 基本介绍

Java BIO 同步阻塞模型,属于传统的 java io 编程,其相关的类和接口在 java.io包下。


服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器).

BIO工作机制

20200401134307494.png

对 BIO 编程流程的梳理

  1. 服务器端启动一个 ServerSocket,注册端口,调用accpet方法监听客户端的Socket连接。
  2. 客户端启动 Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通讯。

这里,编写一些简单的案例便于理解。

BIO编程实例

网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信(绑定IP地址和端口),客户端通过连接操作向服务端监听的端口地址发起连接请求,基于TCP协议下进行三次握手连接,连接成功后,双方通过网络套接字(Socket)进行通信。

功能1:客户端发送一个消息,服务端接口一个消息,通信结束!!

创建客户端对象:

(1)创建一个Socket的通信管道,请求与服务端的端口连接。

(2)从Socket管道中得到一个字节输出流。

(3)把字节流改装成自己需要的流进行数据的发送

客户端代码

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
/**
 目标: Socket网络编程。
 Java提供了一个包:java.net下的类都是用于网络通信。
 Java提供了基于套接字(端口)Socket的网络通信模式,我们基于这种模式就可以直接实现TCP通信。
 只要用Socket通信,那么就是基于TCP可靠传输通信。
 Socket的使用:
 构造器:public Socket(String host, int port)
 方法:  public OutputStream getOutputStream():获取字节输出流
 public InputStream getInputStream() :获取字节输入流
 ServerSocket的使用:
 构造器:public ServerSocket(int port)
 小结:
 通信是很严格的,对方怎么发你就怎么收,对方发多少你就只能收多少!!
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端的启动");
        //创建一个socket通信管道,请求与服务端端口连接
        Socket socket = new Socket("127.0.0.1", 8888);
        //从socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();
        //把字节流改装成自己需要的流进行数据发送
        PrintStream ps = new PrintStream(os);
        ps.println("hi,我是客户端端,我给你发消息了!!");
        ps.flush();
    }
}

创建服务端对象:

(1)注册端口

(2)开始等待接收客户端的连接,得到一个端到端的Socket管道

(3)从Socket管道中得到一个字节输入流。

(4)把字节输入流包装成自己需要的流进行数据的读取。

服务端代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * @PackageName: com.netty.bio.demo1
 * @author: youjp
 * @create: 2020-12-18 10:59
 * @description: 服务端案例
 * @Version: 1.0
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        System.out.println("==服务器的启动==");
        // (1)注册端口
        ServerSocket serverSocket = new ServerSocket(8888);
        //(2)开始在这里暂停等待接收客户端的连接,得到一个端到端的Socket管道
        Socket socket = serverSocket.accept();
        //(3)从Socket管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();
        //(4)把字节输入流包装成自己需要的流进行数据的读取。
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        //(5)读取数据
        String line ;
        if ((line = br.readLine())!=null){
            System.out.println("服务端收到:"+line);
        }
    }
}

启动测试,注意先启动服务端,再启动客户端,不然客户端会报错。

功能2:在功能1的基础上实现消息的多发和多收

功能1的案例中,只能实现客户端发送消息,服务端接收消息,并不能实现反复的收消息和反复的发消息,我们只需要在客户端案例中,加上反复按照行发送消息的逻辑即可!

客户端代码如下

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
 目标: Socket网络编程。
  功能2: 客户端可以反复发,一个服务可以接收无数个客户端消息!!
 小结:服务器如果想要接收多个客户端,那么必须引入线程,一个客户端一个线程处理!!
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端的启动");
        //创建一个socket通信管道,请求与服务端端口连接
        Socket socket = new Socket("127.0.0.1", 8888);
        //从socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();
        InputStream in=socket.getInputStream();
        //把字节流改装成自己需要的流进行数据发送
        PrintStream ps = new PrintStream(os);
        Scanner scanner=new Scanner(System.in);
        while (true){
            System.out.print("请说:");
            String str=scanner.nextLine();
            ps.println(str);
            ps.flush();
        }
    }
}

服务端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @PackageName: com.netty.bio.demo1
* @author: youjp
* @create: 2020-12-18 10:59
* @description: 服务端案例
* @Version: 1.0
*/
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        System.out.println("==服务器的启动==");
        // (1)注册端口
        ServerSocket serverSocket = new ServerSocket(8888);
        //(2)开始在这里暂停等待接收客户端的连接,得到一个端到端的Socket管道
        Socket socket = serverSocket.accept();
        //(3)从Socket管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();
        //(4)把字节输入流包装成自己需要的流进行数据的读取。
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        //(5)读取数据
        String line ;
        while ((line = br.readLine())!=null){
            System.out.println("服务端收到:"+line);
        }
    }
}

本案例中确实可以实现客户端多发多收,但是服务端只能处理一个客户端的请求,因为服务端是单线程的。一次只能与一个客户端进行消息通信。

功能3:使用线程池来接收多个客户端

在上述的案例中,一个服务端只能接收一个客户端的通信请求,那么如果服务端需要处理很多个客户端的消息通信请求应该如何处理呢,此时我们就需要在服务端引入线程了,也就是说客户端每发起一个请求,服务端就创建一个新的线程来处理这个客户端的请求,这样就实现了一个客户端一个线程的模型,图解模式如下:

20200401134307494.png

服务端代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @PackageName: com.netty.bio.demo3
* @author: youjp
* @create: 2020-12-20 17:23
* @description:
* @Version: 1.0
*/
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ExecutorService service= Executors.newCachedThreadPool(); //弹性伸缩线程池
        System.out.println("服务启动----------");
        ServerSocket serverSocket=new ServerSocket(8889);
        while(true){
          final  Socket socket= serverSocket.accept();
             service.submit(new MyThread(socket));//
        }
    }
}
class MyThread implements Runnable{
    private Socket socket;
    public MyThread(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        execute();
    }
    public void execute(){
        System.out.println("分配到线程---"+Thread.currentThread().getName()+"---------------");
        //(3)从Socket管道中得到一个字节输入流。
        InputStream is = null;
        try {
            is = socket.getInputStream();
            //(4)把字节输入流包装成自己需要的流进行数据的读取。
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //(5)读取数据
            String line ;
            while ((line = br.readLine())!=null){
                System.out.print("通过执行线程,--名称---"+Thread.currentThread().getName()+"---------------");
                System.out.println("服务端收到:"+line);
            }
        } catch (IOException e) {
            System.out.println(Thread.currentThread().getName()+"断开连接");
            //e.printStackTrace();
        }
    }
}

客户端

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
 目标: Socket网络编程。
实现:运行多个客户端,向服务端发送消息
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端的启动");
        //创建一个socket通信管道,请求与服务端端口连接
        Socket socket = new Socket("127.0.0.1", 8888);
        //从socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();
        //把字节流改装成自己需要的流进行数据发送
        PrintStream ps = new PrintStream(os);
        Scanner scanner=new Scanner(System.in);
        while (true){
            System.out.print("请说:");
            String str=scanner.nextLine();
            ps.println(str);
            ps.flush();
        }
    }
}

本案例你会发现,每单有客户端对服务端发起连接时,服务端就会创建一个线程进行处理。


当客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出,线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务。


有兴趣的老爷,可以关注我的公众号【一起收破烂】,回复【006】获取2021最新java面试资料以及简历模型120套哦~




相关文章
|
8月前
|
Java
如何理解网络阻塞 I/O:BIO
如何理解网络阻塞 I/O:BIO
107 3
阻塞式/非阻塞式与同步/异步的区别
阻塞式/非阻塞式与同步/异步的区别
96 0
理解阻塞、非阻塞与同步、异步的区别
理解阻塞、非阻塞与同步、异步的区别
理解阻塞、非阻塞与同步、异步的区别
|
调度 C++
进程、线程、并发、并行、同步、异步、阻塞、非阻塞
乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。
|
缓存 监控 Kubernetes
聊聊同步、异步、阻塞、非阻塞以及IO模型
同步、异步、阻塞、非阻塞以及IO模型
|
消息中间件 Java 弹性计算
传统同步阻塞式I/O模型(BIO)
传统BIO编程 网络编程的基本模型是Client/Server模型,就是两个进程之间进行相互通信,Server端提供绑定的IP地址和监听端口,客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,建立成功之后就可以通过Socket通信。
1699 0
|
缓存 Java
同步 异步 阻塞 非阻塞
在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解。具体如下:  序号 问题 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步非阻塞? 7 什么是异步阻塞? 8 什么是异步非阻塞? 散仙不才,在查了一部分资料后,愿试着以通俗易懂的方式
1887 1
同步、异步、阻塞、非阻塞
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication) 真正意义上的 异步IO 是说内核直接将数据拷贝至用户态的内存单元,再通知程序直接去读取数据。
1006 0
同步,异步,阻塞和非阻塞
同步,异步,阻塞和非阻塞的理解
1663 0
|
调度 网络协议 算法
深入理解并发/并行,阻塞/非阻塞,同步/异步
1. 阻塞,非阻塞 首先,阻塞这个词来自操作系统的线程/进程的状态模型中,如下图: 一个线程/进程经历的5个状态,创建,就绪,运行,阻塞,终止。
1475 0