• java BIO深入学习


    一、BIO的工作原理

    传统Io(BIO)的本质就是面向字节流来进行数据传输的
    在这里插入图片描述
    ①:当两个进程之间进行相互通信,我们需要建立一个用于传输数据的管道(输入流、输出流),原来我们传输数据面对的直接就是管道里面一个个字节数据的流动(我们弄了一个 byte 数组,来回进行数据传递),所以说原来的 IO 它面对的就是管道里面的一个数据流动,所以我们说原来的 IO 是面向流的
    ②:我们说传统的 IO 还有一个特点就是,它是单向的。解释一下就是:如果说我们想把目标地点的数据读取到程序中来,我们需要建立一个管道,这个管道我们称为输入流。相应的,如果如果我们程序中有数据想要写到目标地点去,我们也得再建立一个管道,这个管道我们称为输出流。所以我们说传统的 IO 流是单向的

    二、传统的BIO编程实例回顾

    网络编程的基本模型是C/S(客户端/服务器端)模型,也就是两个进程之间的通讯,其中服务端提供位置信(绑定ip地址和端口),客户端通过连接操作向服务器端监听的端口地址发起连接请求,基于TCP协议下进行三次握手连接,连接成功后,双方进行socket通讯。
    传统的同步阻塞模型开发中,服务端ServerSocket负责绑定ip地址,启动监听端口;客户端Socket负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。
    基于BIO模式下的通讯,客户端-服务器端是完全同步,完全耦合的。
    服务器端代码

    public class Server {
        public static void main(String[] args) {
            try {
                System.out.append("服务器端启动。。。");
                //1.定义ServerSocket对象进行服务端的端口注册
                ServerSocket serverSocket = new ServerSocket(8080);
                //2.监听客户端的Socket连接程序
                Socket socket = serverSocket.accept();
                //3.从socket对象当中获取到一个字节输入流对象
                InputStream iStream = socket.getInputStream();
                //打印输出
                int len = 0;
                int ReviceLen = 0;
                //计算机网络数据是以8bit为一个单元进行发送,我们接收到发送方发送的byte数据
                //将其转化为utf-8的格式进行输出
                byte[] recvBuf = new byte[1024];
                while ((ReviceLen = iStream.read(recvBuf)) != -1) {
                    System.out.println("  客户端说:"
                            + new String(recvBuf, 0, ReviceLen, "UTF-8"));
                }
            } catch (Exception e) {
    
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    客户端代码

    public class Client {
        public static void main(String[] args) throws Exception {
            //1.创建socket对象请求服务器的连接
            Socket socket = new Socket("127.0.0.1",8080);
            //2.从socket对象中获取一个字节输出流、
            OutputStream oStream = socket.getOutputStream();
            oStream.write(("你好服务器").getBytes());//以字节流的形式发送数据
            //4.关闭
            oStream.flush();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三、BIO模式下的多发和多收消息

    服务器端不变,客户端:

    public class Client {
        public static void main(String[] args) throws Exception {
            //1.创建socket对象请求服务器的连接
            Socket socket = new Socket("127.0.0.1",8080);
            //2.从socket对象中获取一个字节输出流、
            OutputStream oStream = socket.getOutputStream();
            Scanner scanner = new Scanner(System.in);
            while (true){
                System.out.println("请说....");
                String message = scanner.nextLine();
                oStream.write(message.getBytes());
                //4.关闭
                oStream.flush();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    在这里插入图片描述

    四、BIO模式下接收多个客户端

    在上述的案例当中,一个服务端只能接收一个客户端的通信请求,那么如果服务端需要处理很多个客户端的消息通讯请求应该如何处理呢?

    当我们启动两个客户端,分别去访问服务器端的时候,我们发现服务器端只连接了一个客户端,并且只能和一个客户端进行通信。
    在这里插入图片描述
    在这里插入图片描述

    什么原因导致了我们服务器只能链接一个客户端

    在这里插入图片描述

    那如何解决呢?

    此时就需要在服务端引入线程了,也就是说客户端发起一次请求,服务端就会创建一个新的线程来处理一个新的线程来处理这个客户端的请求,这样就实现了一个客户端一个线程的模型,图解如下:
    在这里插入图片描述

    服务器端改进代码

    public class Server {
        public static void main(String[] args) {
            try {
                System.out.append("服务器端启动。。。");
                //1.定义ServerSocket对象进行服务端的端口注册
                ServerSocket serverSocket = new ServerSocket(8080);
                while (true){
                    //2.监听客户端的Socket连接程序
                    Socket socket = serverSocket.accept();
                    //创建一个独立的线程来处理也客户端的Socket请求
                    new ServerThreadReader(socket).start();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    class ServerThreadReader extends Thread {
        private Socket socket;
    
        public ServerThreadReader(Socket socket) {
            this.socket = socket;
        }
    
        @Override
        public void run() {
            //3.从socket对象当中获取到一个字节输入流对象
            try {
                InputStream iStream = socket.getInputStream();
                //打印输出
                int len = 0;
                int ReviceLen = 0;
                //计算机网络数据是以8bit为一个单元进行发送
                byte[] recvBuf = new byte[1024];
                while ((ReviceLen = iStream.read(recvBuf)) != -1) {
                    System.out.println("  客户端说:"
                            + new String(recvBuf, 0, ReviceLen, "UTF-8"));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    小结

    1.每个socket接受到,都会床架一个新的线程,线程的竞争、切换上下文影响性能。
    2.每个线程都会占用栈空间和cpu资源
    3.并不是每一个socket都进行IO操作,无意义的线程处理
    4.客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程
       栈溢出,线程创建失败,最终导致进程宕机或僵死,从而不能对外提供服务
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    Apollo 应用与源码分析:CyberRT-protobuf
    UNIX 线程相关知识体系
    博迪投资学·投资组合:第六七章的模型总结
    用docker搭载redis集群
    134. 加油站
    Java多并发(二)| cas & synchronized & volatile的内存语义
    使用华为RH2288服务器搭建iSCSI共享存储(含ESXi映射步骤)
    读书笔记:《北大管理课》
    【计算机网络】IP协议分析
    QT day 1
  • 原文地址:https://blog.csdn.net/weixin_39038328/article/details/136339205