IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 慢慢说IO模型:传统BIO -> 正文阅读

[网络协议]慢慢说IO模型:传统BIO

小Q:什么是 IO 模型?

慢慢:我们先来了解下 IO。在 linux 系统中,一切皆是文件,而文件又是一串二进制流。操作系统中有很多的流,我们需要通过一个符号表才能够管理这些流,这个符号表为 fd,即文件描述符表。

所以,Linux 的内核将所有外部设备都看做一个文件来操作,每一个文件的读写操作都会返回一个 fd。

我们每次操作文件,都需要用户空间,内核空间,设备空间进行紧密的配合。那么如何让他们更高效的配合呢?就提出了多种的设计方案,也叫做 IO 模型。


小Q:能具体讲下 BIO 模型吗?

BIO 模型即同步阻塞模型。我们以网络 IO 模型为例。

网络编程的基本模型是 Client/Server 模型,也就是两个进程间相互通信,其中服务端提供位置信息,客户端通过连接操作向服务端监听的地址发送连接请求,通过三次握手建立连接,如果连接成功,则双方可以通过网络套接字进行通信。

在传统的 BIO 模式下,Server 必须先开启一个线程用来监听是否有连接的请求,每当有连接建立时,通过创建线程去处理该链接,从而保证有一个线程是用来专门监听请求,而不会被业务功能所耽误。


小Q:懂了懂了,赶快上代码吧!

服务端程序

public class Server {
    public static void main(String[] args) {
        ServerSocket server = new ServerSocket(8080);   // 开启服务进程
        Socket socket = null;
        while (true) {  // 此线程循环监听是否有连接请求
            socket = server.accept();   // 这里阻塞,知道有连接请求
            new Thread(new ServerHandler(socket)).start();   // 开启一个线程处理请求
        }
        server.close();   // 关闭服务
        server = null;
    }
}

请求处理

public ServerHandler implements Runnable {
    private Socket socket;
    public ServerHandler (Socket socket) {
        this.socket = socket;
    }
    
    @Override
    public void run() {
        BufferedRead in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));  // 从 socket 通道中获取请求的数据
            out = new PrintWriter(this.socket.getOuputStream(), true);
            String body = null;
            while (true) {
                body = in.readLine();  // 从缓冲区中读取一行数据
                if (body == null)
                    break;   // 缓冲区中没有数据
                out.println(body);  // 再写入 socket 中,发送给客户端
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {   
            // 优雅退出
            if (in != null) {
                try {
                    in.close();  // 释放缓冲区
                    in = null;
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
                
            }
            if (out != null) {
                out.close();  // 释放缓冲区
                out = null;
            }
            if (this.socket != null) {
                try {
                    this.socket.close();  // 关闭连接
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
                this.socket = null;
            }
        }
    }
}

客户端:

public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        BufferReader in = null;
        PrintWriter out = null;
        try {
            socket = new Socket("127.0.0.1", 8080);   // 建立连接,发起连接请求
            in = new BufferReader(new InputStreamReader(socket.getInputStream()));  // 监听此缓冲区
            out = new PrintWriter(socket.getOutputStream(), true);
            out.println("QUERY TIME ORDER");   // 向服务器端发送指令
            String s = in.readLine();   // 从缓冲区中获取数据
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 优雅退出
            if (out != null) {
                try {
                    out.close();  // 释放缓冲区
                    out = null;
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
                
            }
            if (in != null) {
                in.close();  // 释放缓冲区
                in = null;
            }
            if (this.socket != null) {
                try {
                    this.socket.close();  // 关闭连接
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
                this.socket = null;
            }
        }
    }
}

小Q:大概理解了,就是用一个线程去监听连接,然后每次有连接都开启一个线程去处理。这样做是不是会有缺陷呀!

慢慢:是的呀。如果突然有大量的请求发到服务器上,那么服务器就得创建大量的线程,这会导致创建资源的不可控,甚至是服务器宕机,这种是绝对不允许的。此外,这种模型也不支持长连接,如果一个连接久久都每释放,那么这个线程就一直被占用,导致后面能创建的线程数变少。


小Q:说到长连接,长连接和短连接的区别是什么呢?

慢慢:短连接的话,每发送完请求并接收到响应后都会自动断开连接。我们都知道,TCP 连接的建立需要经过三次握手,这也导致网络负担的增加,并且握手的数据包往返也消耗了大量的时间。长连接则是通过心跳机制来维护这个连接不会断开,并且每次发送请求都不用经过握手阶段。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-09-10 11:12:15  更:2021-09-10 11:14:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:13:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码