【计网】实现reactor反应堆模型 --- 框架搭建

news2024/11/26 20:46:06

在这里插入图片描述

没有一颗星,
会因为追求梦想而受伤,
当你真心渴望某样东西时,
整个宇宙都会来帮忙。
--- 保罗・戈埃罗 《牧羊少年奇幻之旅》---

实现Reactor反应堆模型

  • 1 前言
  • 2 框架搭建
  • 3 准备工作
  • 4 Reactor类的设计
  • 5 Connection连接接口
  • 6 回调方法

1 前言

到目前为止,我学习了计算机网络,了解了网络传输的过程,理解网络协议栈的层与层之间的关系。实践了使用TCP进行的网络编程,也了解了协议的编写,实际了http协议下的通信过程。

最近学习了五种IO模型,可以通过多路转接EPOLL提高读取效率。

那么现在是否可以将多路转接与网络结合,编写一个高效处理网络请求的反应堆模型Reactor。今天我们搭建基础的结构。

2 框架搭建

在这里插入图片描述
我们想要搭建的是这样的结构:

  1. 最底层是Reactor:负责事件派发,管理connection套接字连接。可以添加监听套接字与普通套接字,其中都有对应的回调方法。可以通过套接字类型赋予连接对应的回调方法。通过多路转接IO获取就绪事件,找到对应connection执行事件。
  2. Connection连接:管理文件描述符的连接对象,内部有这个文件描述符的输入输出缓冲区,回调函数,客户端信息,就绪事件集。等待Reactor调用回调方法。
  3. Listener监听:这是专门管理监听套接字的对象,里面有对于监听套接字的方法,可以获取新连接。作为监听套接字connection的回调方法
  4. HandlerConnection普通套接字 :这是针对普通套接字的对象,里面有对于普通套接字事件就绪的处理方法类。

最底层的就是这三层结构。下面我们来实现这三层结构。

3 准备工作

在实现三层结构之前,我们先对多路转接IO进行封装,让代码尽可能解耦:

对于多路转接,我们设计一个基类,作为上层调用的统一接口。然后继承出子类Epoll poll select,在子类中分别实现对应的方法。

这里只提供了Epoll的封装:

  1. 构造函数:构造时创建EPOLL模型,获得EPOLLfd。
  2. AddEvent:添加事件,调用epoll_ctl_add方法即可。
  3. Wait:获取底层就绪事件,直接使用epoll_wait即可
#pragma once
#include <iostream>
#include <stdlib.h>
#include <sys/epoll.h>
#include "Log.hpp"
#include "Comm.hpp"

using namespace log_ns;

// 多路复用基类
class Mutliplex
{
public:
    Mutliplex(/* args */)
    {
    }
    virtual bool AddEvent(int fd, uint32_t events) = 0;
    virtual int Wait(struct epoll_event revs[], int num, int timeout) = 0;
    ~Mutliplex()
    {
    }
};

// epoll poll select基类
class Epoller : public Mutliplex
{
private:
    static const int size = 128;

public:
    Epoller()
    {
        _epollfd = ::epoll_create(size);
        if (_epollfd < 0)
        {
            LOG(ERROR, "epoll create failed!\n");
            exit(EPOLL_CREATE);
        }
    }
    std::string EventToString(uint32_t revents)
    {
        std::string ret;
        if (revents & EPOLLIN)
            ret += "EPOLLIN";
        if (revents & EPOLLOUT)
            ret += "| EPOLLOUT";
        return ret;
    }
    bool AddEvent(int fd, uint32_t events)
    {
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = events;
        int n = ::epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev);
        if (n < 0)
        {
            LOG(ERROR, "epoll_ctl add failed , errno:%d", errno);
            return -1;
        }
        LOG(INFO, "epoll_ctl add fd:%d , events:%s\n", fd, EventToString(events).c_str());
        return true;
    }
    int Wait(struct epoll_event revs[], int num, int timeout)
    {
        return ::epoll_wait(_epollfd, revs, num, timeout);
    }
    ~Epoller()
    {
    }

private:
    int _epollfd;
};

4 Reactor类的设计

之前的TcpServer等服务端都要在内部封装_listensock。如果封装了监听套接字那么代码结构就定型了,就必须要有对监听套接字的处理。而这里我们想将Reactor设计一个管理connection连接的类,不需要针对监听套接字进行特殊处理

成员变量:

  1. 通过fd映射Connection*对象的哈希表_conn
  2. 判断是否启动bool isrunning
  3. 构建一个Multipex对象 , 构造时建立epoll指针,负责处理多路转接IO
  4. 就绪事件组struct epoll_event revs[gnum]
  5. 针对监听套接字的方法集,在添加连接时可以将方法设置进入connection
  6. 针对普通套接字的方法集

回调方法的类型为using handler_t = std::function<void(Connection *conn)>;

#pragma once
#include <string>
#include <iostream>
#include <memory>
#include <unordered_map>

#include "Connection.hpp"
#include "Epoller.hpp"

using namespace log_ns;

class Reactor
{
private:
    static const int gnum = 128;

public:
    Reactor() : _epoller(std::make_unique<Epoller>()), _isrunning(false)
    {
    }
    void SetOnNormal(handler_t OnRecver, handler_t OnSender, handler_t OnExcepeter)
    {
        _OnRecver = OnRecver;
        _OnSender = OnSender;
        _OnExcepeter = OnExcepeter;
    }
    void SetOnConnect(handler_t OnConnect)
    {
        _OnConnect = OnConnect;
    }
    // 加入连接
    void AddConnection(int fd, uint32_t events, const InetAddr &addr, int type)
    {
    }
    void Dispatcher()
    {
    }

    ~Reactor()
    {
    }

private:
    // fd 映射连接表
    std::unordered_map<int, Connection *> _conn;
    // 是否启动
    bool _isrunning;
    std::unique_ptr<Mutliplex> _epoller;
    // 事件数组
    struct epoll_event revs[gnum];
    //_listen新连接到来
    handler_t _OnConnect;
    // 处理普通fd IO
    handler_t _OnRecver;
    handler_t _OnSender;
    handler_t _OnExcepeter;
};

  • Addconnection接口 :首先通过 fd events 与客户端信息和连接类型建立connection , 进行设置对应的事件集, 然后根据type判断类型,设置connection的上层处理回调方法。注意这里要对 connReactor进行关联 !后续connection的模块进行讲解,设置addr方便打印日志(可以知道是哪一个客户端);然后通过fd events 托管给epoll 进行添加事件 。最后将连接放入哈希表中。
  • IsConnExists判断当前连接是否存在
  • Dispatch()事件派发接口:进行while循环,获取底层哪些事件就绪 储存在成员变量struct epoll_event revs[gnum],根据返回值 n 对n个事件进行处理!这里只处理 ERR HUP IN OUT 使用if语句ERR HUP直接设置为IN OUT后续统一处理IN事件就绪 事件派发 通过_conn[fd]找到对应连接 执行对应事件的回调函数(注意保证连接存在 且 回调方法存在)。

完整代码如下:

#pragma once
#include <string>
#include <iostream>
#include <memory>
#include <unordered_map>

#include "Connection.hpp"
#include "Epoller.hpp"

using namespace log_ns;

class Reactor
{
private:
    static const int gnum = 128;

public:
    Reactor() : _epoller(std::make_unique<Epoller>()), _isrunning(false)
    {
    }
    void SetOnNormal(handler_t OnRecver, handler_t OnSender, handler_t OnExcepeter)
    {
        _OnRecver = OnRecver;
        _OnSender = OnSender;
        _OnExcepeter = OnExcepeter;
    }
    void SetOnConnect(handler_t OnConnect)
    {
        _OnConnect = OnConnect;
    }
    // 加入连接
    void AddConnection(int fd, uint32_t events, const InetAddr &addr, int type)
    {
        // 1. 通过 fd  构建一个 connection指针 set对应的事件集
        Connection *conn = new Connection(fd);
        conn->SetReactor(this);
        conn->SetEvents(events);
        conn->SetConnectionType(type);
        conn->SetAddr(addr);
        // 2. TODO 设置对connection的上层处理 设置回调方法
        if (conn->Type() == ListenConnection)
        {
            conn->RegisterHandler(_OnConnect, nullptr, nullptr); // 设置方法
        }
        else
        {
            conn->RegisterHandler(_OnRecver, _OnSender, _OnExcepeter); // 设置方法
        }

        // 3. fd 与 events 托管给epoll 添加事件 出错直接 return;
        int n = _epoller->AddEvent(fd, events);
        // 4. 托管给_connection
        _conn.insert(std::make_pair(fd, conn));

        // 添加连接成功
    }
    // 判断连接是否存在
    bool IsConnExist(int fd)
    {
        return _conn.find(fd) != _conn.end();
    }
    void LoopOnce(int timeout)
    {
        // 获取底层事件
        int n = _epoller->Wait(revs, gnum, -1);
        for (int i = 0; i < n; i++)
        {
            // 文件描述符
            int fd = revs[i].data.fd;
            // 就绪事件
            uint32_t revents = revs[i].events;
            // 处理IN OUT ERR HUP
            if (revents & EPOLLERR)
                revents |= (EPOLLIN | EPOLLOUT);
            if (revents & EPOLLHUP)
                revents |= (EPOLLIN | EPOLLOUT);
            if (revents & EPOLLIN)
            {
                // 调用回调方法
                if (IsConnExist(fd) && _conn[fd]->_handler_recver)
                    _conn[fd]->_handler_recver(_conn[fd]);
            }
            if (revents & EPOLLOUT)
            {
                // 调用回调方法
                if (IsConnExist(fd) && _conn[fd]->_handler_sender)
                    _conn[fd]->_handler_sender(_conn[fd]);
            }
        }
    }

    void Dispatcher()
    {
        _isrunning = true;
        int timeout = -1;
        while (true)
        {
            LoopOnce(timeout);
            PrintDebug();//打印托管的fd列表
        }
        _isrunning = false;
    }
    void PrintDebug()
    {
        std::string s = "已建立的连接:";
        for (auto &conn : _conn)
        {
            s += std::to_string(conn.first) + ' ';
        }
        LOG(DEBUG, "epoll 管理的fd列表: %s\n", s.c_str());
    }
    ~Reactor()
    {
    }

private:
    // fd 映射连接表
    std::unordered_map<int, Connection *> _conn;
    // 是否启动
    bool _isrunning;
    std::unique_ptr<Mutliplex> _epoller;
    // 事件数组
    struct epoll_event revs[gnum];
    //_listen新连接到来
    handler_t _OnConnect;
    // 处理普通fd IO
    handler_t _OnRecver;
    handler_t _OnSender;
    handler_t _OnExcepeter;
};

5 Connection连接接口

  1. 成员变量
    • 文件描述符fd
    • 需要关心的事件集 events
    • 输入缓冲区 输出缓冲区
    • 三种事件的回调方法
    • 设置一个Reactor* _R
  2. SetEvents接口:通过传入events 初始化 events
  3. Events接口返回事件集
  4. Sockfd返回对应fd
  5. RegisterHandler接口快速设置回调方法
  6. SetReactor(Reactor* R)接口 connection与Reactor进行绑定,执行自己属于的Reactor

对于这个Reactor* _R 指针,是监听套装字获取到连接时发挥作用。当监听套接字的事件就绪,在回调方法中可以通过参数Connection取出内部的_R指针,找到对应的Reactor,进行AddConnection操作

#pragma once

#include <iostream>
#include <string>
#include <functional>

#include "InetAddr.hpp"

class Connection;
class Reactor;

using handler_t = std::function<void(Connection *conn)>;

#define ListenConnection 0
#define NormalConnection 1

class Connection
{
public:
    Connection(int fd) : _sockfd(fd)
    {
    }
    void RegisterHandler(handler_t recver, handler_t sender, handler_t excepter)
    {
        _handler_recver = recver;     // 处理读取
        _handler_sender = sender;     // 处理写入
        _handler_excepter = excepter; // 处理异常
    }
    void SetEvents(uint32_t events)
    {
        _events = events;
    }
    void SetAddr(const InetAddr &addr)
    {
        _addr = addr;
    }
    int Sockfd()
    {
        return _sockfd;
    }
    uint32_t Events()
    {
        return _events;
    }
    int Type()
    {
        return _type;
    }
    void SetReactor(Reactor *R)
    {
        _R = R;
    }
    void SetConnectionType(int type)
    {
        _type = type;
    }
    Reactor *GetReactor()
    {
        return _R;
    }
    InetAddr GerInetAddr()
    {
        return _addr;
    }
    void AppendInbuffer(const std::string &in)
    {
        _inbuffer += in;
    }
    std::string &Inbuffer()
    {
        return _inbuffer;
    }
    ~Connection()
    {
    }

private:
    int _sockfd;            // 套接字fd
    uint32_t _events;       // 事件集
    std::string _inbuffer;  // 输入缓冲区
    std::string _outbuffer; // 输出缓冲区
    Reactor *_R;
    int _type;
    InetAddr _addr;

public:
    handler_t _handler_recver;   // 处理读取
    handler_t _handler_sender;   // 处理写入
    handler_t _handler_excepter; // 处理异常
};

6 回调方法

这里需要两种回调方法类,一种针对监听套接字,一种针对普通套接字。

Listener
  1. Listener统一管理Tcp连接模块,管理_listensock
  2. 成员变量 :
    • std::unique_ptr _listensock Tcp套接字对象
    • int _port;端口号
  3. 通过端口号进行构造TcpSocket
  4. ListenSock接口返回_listensock的fd。
  5. Accepter(conn* , int* code)方法获取连接并得到文件描述符 (这里采用ET模式)首先将listensockfd 读取设置为非阻塞读取,然后进行while(true)进行非阻塞读取 ,根据Accepter返回的错误码通过code返回 通过错误码进行判断,当读取到一个新的fd时,通过conn的Reactor指针调用AddConnection 加入新连接!
#pragma once
#include <memory>
#include <iostream>
#include "Socket.hpp"
#include "Connection.hpp"

using namespace log_ns;
using namespace socket_ns;

// 处理listen套接字的读写
class Listener
{
public:
    Listener(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>(port))
    {
        _listensock->BuildListenSocket(_port);
    }
    int ListenSockfd()
    {
        return _listensock->GetSockfd();
    }
    void Accepter(Connection *conn)
    {
        LOG(DEBUG, "%d socket ready\n", conn->Sockfd());
        // 非阻塞式读取
        while (true)
        {
            errno = 0;
            int code = 0;
            InetAddr addr;
            int sockfd = _listensock->Accepter(&addr, &code);
            if (sockfd > 0)
            {
                LOG(INFO, "成功获取连接, 客户端:%s sockfd:%d\n", addr.AddrStr().c_str(), sockfd);
                conn->GetReactor()->AddConnection(sockfd, EPOLLIN | EPOLLET, addr, NormalConnection);
            }
            else
            {
                if (code == EWOULDBLOCK)
                {
                    // 读取完毕
                    LOG(INFO, "底层数据全部读取完毕!\n");
                    break;
                }
                // 信号中断
                else if (code == EINTR)
                {
                    continue;
                }
                else
                {
                    LOG(ERROR, "获取连接失败!\n");
                    break;
                }
            }
        }
    }

    ~Listener()
    {
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;
};
HandlerConnection
  1. 处理普通连接读写问题,这个的设计就比较简单了,注意其只复杂数据的读取,协议解析需要交给上层进行处理!
  2. HandlerRecver(conn*):我们先实现读取的逻辑!
  3. HandlerSender(conn*):后续实现
  4. HandlerExcepter(conn*):后续实现
#include <sys/types.h>
#include <sys/socket.h>
// 不应该让HandlerConnection处理报文
class HandlerConnection
{
private:
    const static int buffersize = 512;

public:
    HandlerConnection(handler_t process) : _process(process)
    {
    }
    void Recver(Connection *conn)
    {
        // LOG(DEBUG , "client发送信息: %d\n" , conn->Sockfd());
        // 进行正常读写 --- 非阻塞读取

        while (true)
        {
            char buffer[buffersize];
            int n = ::recv(conn->Sockfd(), buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                // buffer是一个数据块 添加到conn的输入缓冲区中
                buffer[n] = 0;
                conn->AppendInbuffer(buffer);
                // 数据交给上层处理
            }
            else if (n == 0)
            {
                // 连接断开
                LOG(INFO, "客户端[%s]退出, 服务器准备关闭fd: %d\n", conn->GerInetAddr().AddrStr().c_str(), conn->Sockfd());
                conn->_handler_excepter(conn); // 统一执行异常处理
            }
            else
            {
                // 本轮数据读完了
                if (errno == EWOULDBLOCK)
                {
                    // 这是唯一出口
                    break;
                }
                // 信号中断
                else if (errno == EINTR)
                {
                    continue;
                }
                // 出现异常
                else
                {
                    conn->_handler_excepter(conn);
                    return;
                }
            }
        }
        // 读取完毕,我们应该处理数据了!
        // 加入协议
        std::cout << "Inbuffer 内容:" << conn->Inbuffer() << std::endl;
        _process(conn);
    }
    void Sender(Connection *conn)
    {
    }
    void Excepter(Connection *conn)
    {
    }
    ~HandlerConnection()
    {
    }

private:
    handler_t _process;
};

至此,Reactor反应堆模型的框架已经搭建好了,下一篇文章我们将在这个的基础之上进行协议解析与数据处理!并设计如何将数据发回。这里只是简单的实现读取数据的逻辑!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2236167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

minikube 的 Kubernetes 入门教程--(五)

本文记录 Minikube 在 Kubernetes 上安装 WordPress 和 MySQL。 这两个应用都使用 PersistentVolumes 和 PersistentVolumeClaims 保存数据。 在深入这些步骤之前&#xff0c;先分享来自kubernetes.io教程。 链接>>使用持久卷部署 WordPress 和 MySQL | Kubernetes 获…

算法详解——链表的归并排序非递归解法

算法详解——链表的归并排序非递归解法 本文使用倍增法加上归并排序操作实现了对链表的快速排序&#xff0c;比起一般的递归式归并排序要节省空间并且实现要简单的多&#xff0c;比起一般的迭代式归并排序实现也要简单。 1. 题目假设 给定链表的头结点 head &#xff0c;请将其…

【网络-交换机】生成树协议、环路检测

路由优先级 路由优先级决定了在多种可达的路由类型中&#xff0c;哪种路由将被用来转发数据包。路由优先级值越低&#xff0c;对应路由的优先级越高&#xff0c;优先级值255表示对应的路由不可达。一般情况下&#xff0c;静态路由的优先级为1&#xff0c;OSPF路由优先级为110&a…

确定图像的熵和各向异性 Halcon entropy_gray 解析

1、图像的熵 1.1 介绍 图像熵&#xff08;image entropy&#xff09;是图像“繁忙”程度的估计值&#xff0c;它表示为图像灰度级集合的比特平均数&#xff0c;单位比特/像素&#xff0c;也描述了图像信源的平均信息量。熵指的是体系的混乱程度&#xff0c;对于图像而言&#…

数字后端零基础入门系列 | Innovus零基础LAB学习Day9

Module 16 Wire Editing 这个章节的学习目标是学习如何在innovus中手工画线&#xff0c;切断一根线&#xff0c;换孔&#xff0c;更改一条net shape的layer和width等等。这个技能是每个数字IC后端工程师必须具备的。因为项目后期都需要这些技能来修复DRC和做一些手工custom走线…

除草机器人算法以及技术详解!

算法详解 图像识别与目标检测算法 Yolo算法&#xff1a;这是目标检测领域的一种常用算法&#xff0c;通过卷积神经网络对输入图像进行处理&#xff0c;将图像划分为多个网格&#xff0c;每个网格生成预测框&#xff0c;并通过非极大值抑制&#xff08;NMS&#xff09;筛选出最…

ProCalun卡伦纯天然万用膏,全家的皮肤健康守护

受季节交替、生活环境变化、空气污染等方面因素的影响&#xff0c;加上作息不规律导致的免疫力降低&#xff0c;我们或多或少会出现一些如湿疹、痤疮、瘙痒之类的皮肤问题&#xff0c;且反复概率很高。很多人盲目用药&#xff0c;甚至诱发激素依赖性皮炎。所以近年来&#xff0…

Vue 自定义icon组件封装SVG图标

通过自定义子组件CustomIcon.vue使用SVG图标&#xff0c;相比iconfont下载文件、重新替换更节省时间。 子组件包括&#xff1a; 1. Icons.vue 存放所有SVG图标的path 2. CustomIcon.vue 通过icon的id索引对应的图标 使用的时候需要将 <Icons></Icons> 引到使用的…

wireshark工具使用

复制数据 1.右键展开整帧数据 2.复制“所有可见项目” mark标记数据 标记&#xff1a; 跳转&#xff1a; 保存成文件&#xff1a; 文件–>导出特定分组—>Marked packets only

【SpringCloud】SpringBoot集成Swagger 常用Swagger注解

概述&#xff1a;SpringBoot集成Swagger 常用Swagger注解 导语 相信无论是前端还是后端开发&#xff0c;都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力&#xff0c;经常来不及更新。其实无论是前…

Webserver(4.9)本地套接字的通信

目录 本地套接字 本地套接字 TCP\UDP实现不同主机、网络通信 本地套接字实现本地的进程间的通信&#xff0c;类似的&#xff0c;一般采用TCP的通信流程 生成套接字文件 #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h&…

[spring源码]spring配置类解析

解析配置类 在启动Spring时&#xff0c;需要传入一个AppConfig.class给ApplicationContext&#xff0c;ApplicationContext会根据AppConfig类封装为一个BeanDefinition&#xff0c;这种BeanDefinition我们把它称为配置类BeanDefinition AnnotationConfigApplicationContext a…

uni-app跨域set-cookie

set-cookie的值是作为一个权限控制的 首先&#xff0c;无论什么接口都会返回一个set-cookie&#xff0c;但未登录时&#xff0c;set-cookie是没有任何权限的 其次&#xff0c;登录接口请求时会修改set-cookie&#xff0c;并且在后续其他接口发起请求时&#xff0c;会在请求头…

chrdevbase驱动之Makefile优化(指定路径复制、删除文件)

对于学习嵌入式linux驱动篇的chrdevbase虚拟设备驱动时&#xff0c;需要将chrdevbase.c编译成.ko文件&#xff0c;应用层程序里需要把chrdevbaseAPP编译成chrdevbaseAPP可执行文件&#xff0c;此外还需要将生成的*.ko *APP文件拷贝至指定目录下&#xff0c;每次修改或者编译代码…

kafka实时返回浏览数据

在安装完kafka(Docker安装kafka_docker 部署kafka-CSDN博客)&#xff0c;查看容器是否启动&#xff1a; docker ps | grep -E kafka|zookeeper 再用python开启服务 from fastapi import FastAPI, Request from kafka import KafkaProducer import kafka import json import …

使用QtWebEngine的Mac应用如何发布App Store

前言 因为QtWebEngine时第三方包,苹果并不直接支持进行App Store上签名和发布,所以构建和发布一个基于使用QtWebEngine的应用程序并不容易,这里我们对Qt 5.8稍微做一些修改,以便让我们的基于QtWeb引擎的应用程序并让签名能够得到苹果的许可。 QtWebEngine提供了C++和Qml的…

C++虚继承演示

在继承中如果出现&#xff1a; 这种情况&#xff0c;B和C都继承了A&#xff0c;D继承了B、C 在D中访问A的成员会出现&#xff1a; 这样的警告 是因为在继承时A出现两条分支&#xff1a;ABD、ACD 编译器不知道访问的A中的元素是经过B继承还是C继承 所以B、C在继承A时要用到…

【赵渝强老师】Redis的RDB数据持久化

Redis 是内存数据库&#xff0c;如果不将内存中的数据库状态保存到磁盘&#xff0c;那么一旦服务器进程退出会造成服务器中的数据库状态也会消失。所以 Redis 提供了数据持久化功能。Redis支持两种方式的持久化&#xff0c;一种是RDB方式&#xff1b;另一种是AOF&#xff08;ap…

【计算机网络】章节 知识点总结

一、计算机网络概述 1. 计算机网络向用户提供的两个最重要的功能&#xff1a;连通性、共享 2. 因特网发展的三个阶段&#xff1a; 第一阶段&#xff1a;从单个网络 ARPANET 向互联网发展的过程。1983 年 TCP/IP 协议成为 ARPANET 上的标准协议。第二阶段&#xff1a;建成三级…

Linux基础(十)——磁盘分区、格式化、检验和挂载

磁盘分区、格式化、检验和挂载 1.观察磁盘的分区状态2.UUID3.磁盘分区&#xff08;gdisk/fdisk&#xff09;3.1 gdisk3.2 fdisk 4.磁盘的格式化4.1 XFS文件系统的格式化4.2 ext4文件系统的格式化 5.文件系统的救援6.文件系统的挂载与卸载6.1 挂载6.2 卸载 7.设置开机挂载8.特殊…