当前位置: 首页 > news >正文

CS144-Lab6

概述

在本周的实验中,你将在现有的NetworkInterface基础上实现一个IP路由器,从而结束本课程。路由器有几个网络接口,可以在其中任何一个接口上接收互联网数据报。路由器的工作是根据路由表转发它得到的数据报:一个规则列表,它告诉路由器,对于任何给定的数据报:

  • 发送到哪个接口;
  • 下一跳的IP地址 ;

你的工作是实现一个路由器,它可以为任何给定的数据报计算出这两件事。(你不需要实现设置路由表的算法,例如RIP、OSPF、BGP或SDN控制器,只需要实现跟随路由表的算法)。

你对路由器的实现将使用带有新的Router类的Sponge库,以及在模拟网络中检查你的路由器功能的测试。实验6建立在你在实验5中对NetworkInterface的实现之上,但不使用你在实验0-4中实现的TCP栈。IP路由器不需要知道任何关于TCP、ARP或以太网的信息(仅限IP)。我们希望你的实现将需要大约25-30行的代码。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3DONudSr-1677328903313)(null)]

图1:路由器包含多个网络接口,可以在其中任何一个接口上接收IP数据报。路由器将接收到的任何数据报转发到相应出站接口上的下一跳,路由表告诉路由器如何做出这个决定。

开始

  1. 请确保你已经提交了你在实验5中的所有解决方案。请不要修改libsponge目录顶层以外的任何文件,或者webget.cc。(请不要添加代码所依赖的额外文件。)否则,你可能会在合并实验6的启动代码时遇到麻烦。
  2. 在实验作业的存储库中,运行git fetch来检索实验作业的最新版本。
  3. 通过运行git merge origin/lab6-startercode,下载实验6的启动代码。
  4. build目录中,编译源代码:make(编译时可以运行make -j4以使用四个处理器)。
  5. build目录外,打开并开始编辑writeups/lab6.md文件。这是你实验报告的模板,将包含在你提交的内容中。

实现路由器

在本实验中,你将实现一个Router类,它可以:

  • 跟踪路由表(转发规则或路由列表),并
  • 转发它收到的每个数据报:
    • 转发到正确的下一跳
    • 在正确的出站NetworkInterface

你的实现将被添加到router.hh和router.cc骨架文件中。在你开始编码之前,请查看新的Router类的文档。

下面是你要实现的两个方法,以及我们对每个方法的期望:

void add_route(const uint32_t route_prefix,const uint8_t prefix_length,const optional<Address> next_hop,const size_t interface_num);

这个方法将一条路由添加到路由表中。你要在Router类中添加一个数据结构作为私有成员来存储这些信息。这个方法所要做的就是保存路由,以供以后使用。

路由的各个部分是什么意思?

路由是一个”匹配——行动”规则:它告诉路由器,如果一个数据报前往一个特定的网络(一个IP地址范围),并且如果该路由被选为最具体的匹配路由,那么路由器应该把数据报转发到特定接口上的特定下一跳。

“匹配”:数据报是前往这个网络的吗?route_prefixprefix_length共同指定了一个可能包括数据报目的地的IP地址范围(一个网络)。route_prefix是一个32位数字的IP地址。prefix_length是一个介于0和32(包括32)之间的数字;它告诉路由器路由前缀中有多少最高有效位是有效的。例如,要表达一个到网络”18.47.0.0/16”的路由(这与前两个字节为18和47的任何32位IP地址匹配),路由前缀将是305070080(18×224+47×216),前缀长度是16。任何以”18.47.x.y”为目的地的数据报都会匹配。

**“行动”:如果路由匹配并被选中,该怎么做。**如果路由器直接连接到有关的网络,next_hop将是一个空的可选项;在这种情况下,next_hop是数据报的目标地址。但如果路由器是通过其他路由器连接到有关网络的,则next_hop将包含路径中下一路由器的IP地址。interface_num给出了路由器NetworkInterface的索引,它用来将数据报发送到下一跳。你可以用interface(interface_num)方法访问这个接口。

void route_one_datagram(InternetDatagram &dgram);

这里是橡胶与道路的交汇处。这个方法需要将数据报路由到下一跳,从适当的接口传出。它需要实现IP路由器的”最长前缀匹配”逻辑,以找到最佳路由,这意味着:

  • 路由器搜索路由表,以找到与数据报的目的地址相匹配的路由。我们所说的”匹配”是指目的地址的最高有效prefix_length比特与route_prefix的最高有效prefix_length比特相同的。
  • 在匹配的路由中,路由器选择具有最大prefix_length的路由,这就是最长前缀匹配路由。
  • 如果没有匹配的路由,路由器会丢弃数据报。
  • 路由器会递减数据报的TTL(生存时间)。如果TTL已经为零,或在递减后为零,路由器应该放弃该数据报。
  • 否则,路由器将修改后的数据报通过适当的接口(interface(interface_num).send_datagram())发送到适当的下一跳。

在这个互联网的设计中,有个优点(或至少是一种成功的抽象):路由器从不考虑TCP、ARP或以太网帧。路由器甚至不知道链路层是什么样子的。路由器只考虑互联网数据包,并且只通过NetworkInterface抽象与链路层进行交互。当涉及到”链路层地址是如何解决的?”或”链路层是否有自己的不同于IP的寻址方案?”或”链路层帧的格式是什么?”或”数据报的有效载荷是什么意思?”等问题时,路由器根本不关心。

//! \brief A wrapper for NetworkInterface that makes the host-side
//! interface asynchronous: instead of returning received datagrams
//! immediately (from the `recv_frame` method), it stores them for
//! later retrieval. Otherwise, behaves identically to the underlying
//! implementation of NetworkInterface.
class AsyncNetworkInterface : public NetworkInterface {std::queue<InternetDatagram> _datagrams_out{};public:using NetworkInterface::NetworkInterface;//! Construct from a NetworkInterfaceAsyncNetworkInterface(NetworkInterface &&interface) : NetworkInterface(interface) {}//! \brief Receives and Ethernet frame and responds appropriately.//! - If type is IPv4, pushes to the `datagrams_out` queue for later retrieval by the owner.//! - If type is ARP request, learn a mapping from the "sender" fields, and send an ARP reply.//! - If type is ARP reply, learn a mapping from the "target" fields.//!//! \param[in] frame the incoming Ethernet framevoid recv_frame(const EthernetFrame &frame) {auto optional_dgram = NetworkInterface::recv_frame(frame);if (optional_dgram.has_value()) {_datagrams_out.push(std::move(optional_dgram.value()));}};//! Access queue of Internet datagrams that have been receivedstd::queue<InternetDatagram> &datagrams_out() { return _datagrams_out; }
};struct route_rule{uint32_t route_prefix{};uint8_t prefix_length{};std::optional<Address> next_hop;size_t interface_num{};route_rule(const uint32_t _route_prefix,const uint8_t _prefix_length,const std::optional<Address> _next_hop,const size_t _interface_num):route_prefix(_route_prefix),prefix_length(_prefix_length),next_hop(_next_hop),interface_num(_interface_num){}
};//! \brief A router that has multiple network interfaces and
//! performs longest-prefix-match routing between them.
class Router {//! The router's collection of network interfacesstd::vector<AsyncNetworkInterface> _interfaces{};//! Send a single datagram from the appropriate outbound interface to the next hop,//! as specified by the route with the longest prefix_length that matches the//! datagram's destination address.void route_one_datagram(InternetDatagram &dgram);std::vector<route_rule> _rules{};public://! Add an interface to the router//! \param[in] interface an already-constructed network interface//! \returns The index of the interface after it has been added to the routersize_t add_interface(AsyncNetworkInterface &&interface) {_interfaces.push_back(std::move(interface));return _interfaces.size() - 1;}//! Access an interface by indexAsyncNetworkInterface &interface(const size_t N) { return _interfaces.at(N); }//! Add a route (a forwarding rule)void add_route(const uint32_t route_prefix,const uint8_t prefix_length,const std::optional<Address> next_hop,const size_t interface_num);//! Route packets between the interfacesvoid route();bool prefix_equal(uint32_t ip1, uint32_t ip2, uint8_t prefix_length);
};

这段代码实现了一个多接口的路由器,其中每个接口都有一个异步接收数据报的队列。路由器的功能是根据最长前缀匹配算法对接收到的每个数据报进行路由,并将其发送到下一跳,以使其达到目标地址。

AsyncNetworkInterface是一个对NetworkInterface的封装,它具有一个异步队列_datagrams_out,在接收到数据报后将其推送到队列中。它还覆盖了NetworkInterfacerecv_frame方法,以便将数据报推送到队列中,以供稍后检索。

Router类有一个包含所有网络接口的向量 _interfaces,一个包含所有路由表规则的向量 _rules,以及一个add_interface方法,用于添加接口。路由器还实现了一个add_route方法,该方法接受路由前缀、路由前缀长度、下一跳IP地址(可选)以及出站接口的索引。它将所有路由规则存储在一个向量中,以便在进行数据报路由时进行查找。此外,还实现了一个route方法,用于将路由器的所有接口一起轮询,处理每个接口上接收到的数据报,并调用route_one_datagram方法将其路由到下一跳。最后,还实现了一个prefix_equal方法,用于检查两个IP地址的前缀是否相同。

在路由表的最长前缀匹配算法中,对于目的地址,路由器从路由表中选择最具体的路由,该路由匹配前缀长度最长,而且其匹配的前缀位数与目标地址的前缀位数相同。prefix_equal方法用于比较两个IP地址的前缀。

//! \param[in] route_prefix The "up-to-32-bit" IPv4 address prefix to match the datagram's destination address against
//! \param[in] prefix_length For this route to be applicable, how many high-order (most-significant) bits of the route_prefix will need to match the corresponding bits of the datagram's destination address?
//! \param[in] next_hop The IP address of the next hop. Will be empty if the network is directly attached to the router (in which case, the next hop address should be the datagram's final destination).
//! \param[in] interface_num The index of the interface to send the datagram out on.
void Router::add_route(const uint32_t route_prefix,const uint8_t prefix_length,const optional<Address> next_hop,const size_t interface_num) {cerr << "DEBUG: adding route " << Address::from_ipv4_numeric(route_prefix).ip() << "/" << int(prefix_length)<< " => " << (next_hop.has_value() ? next_hop->ip() : "(direct)") << " on interface " << interface_num << "\n";route_rule r(route_prefix,prefix_length,next_hop,interface_num);_rules.push_back(move(r));
}

Router::add_route 函数添加一个路由规则,包含路由表项中的路由前缀,前缀长度,下一跳地址(如果有),以及数据包的输出接口索引。这个函数打印了调试信息,用于检查添加的路由规则。

//! \param[in] dgram The datagram to be routed
void Router::route_one_datagram(InternetDatagram &dgram) {uint32_t dst = dgram.header().dst;optional<Address> next_hop{};size_t interface_num{};optional<uint8_t> best_by_far{};for(auto& r: _rules) {if((!best_by_far.has_value() || r.prefix_length>best_by_far.value()) && prefix_equal(dst,r.route_prefix,r.prefix_length)){next_hop = r.next_hop;interface_num = r.interface_num;best_by_far = r.prefix_length;}}if(best_by_far.has_value()){if(dgram.header().ttl>1) {dgram.header().ttl--;if(next_hop.has_value()) {interface(interface_num).send_datagram(dgram,next_hop.value());} // if not have next_hop, the next_hop is dgram's destination hopelse {interface(interface_num).send_datagram(dgram,Address::from_ipv4_numeric(dst));}        }}return;
}

Router::route_one_datagram 函数根据路由表将数据报路由到下一个合适的接口。它使用路由表中最长前缀匹配的规则来查找应该用哪个输出接口。如果找到合适的路由规则,则检查数据报的 TTL 是否大于1。如果是,减少TTL并将数据报发送到下一个合适的接口。如果找不到合适的路由规则,数据报将被丢弃。

void Router::route() {// Go through all the interfaces, and route every incoming datagram to its proper outgoing interface.for (auto &interface : _interfaces) {auto &queue = interface.datagrams_out();while (not queue.empty()) {route_one_datagram(queue.front());queue.pop();}}
}

Router::route 函数遍历路由器的所有网络接口,将每个接口的入队数据报依次路由到正确的输出接口上。

bool Router::prefix_equal(uint32_t ip1, uint32_t ip2, uint8_t prefix_length) {if(prefix_length==0){return true;} else {return (ip1 >> (32-prefix_length)) == (ip2 >> (32-prefix_length));}
}

Router::prefix_equal 函数用于比较两个IP地址的最高 prefix_length 位是否相等。如果前缀长度为0,将返回 true。如果前缀长度不为0,则将两个地址右移(32-prefix_length)位,并比较结果是否相等。

测试

你可以通过运行make checklab6来测试你的实现。这将在特定的模拟网络中测试路由器,如图2所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RIREzhCu-1677328905242)(null)]

图2:应用/网络模拟器工具中使用的模拟测试网络,也是由make check lab6运行的。 (有趣的事实:uun网络是David Mazieres的互联网切片,于1993年分配。whois工具或链接的网站可以用来查询谁控制了每个IP地址的分配)。

Q & A

  • 我应该用什么数据结构来记录路由表?

    由你决定! 但不需要太过疯狂。每个数据报需要做O(n)个工作是完全可以接受的,其中n是路由表的条目数。如果你想做一些更有效的事情,我们鼓励你在优化之前先得到一个有效的实现,并仔细记录和评论你选择的任何实现。

  • 如何将以地址对象形式出现的IP地址转换为可以写入ARP消息的32位原始整数?

    使用Address::ipv4_numeric()方法。

  • 如何将一个以原始32位整数形式出现的IP地址转换为一个地址对象?

    使用 Address::from_ipv4_numeric()方法。

  • 如何将一个32位IP地址的最高n位(其中0≤n≤32) 与另一个32位IP地址的最重要的n位进行比较?

    这可能是这项任务中”最棘手”的部分,因为要让逻辑正确。也许值得在C++中写一个小的测试程序(一个简短的独立程序)或者在Sponge中添加一个测试,以验证你对相关的C++操作符的理解,并仔细检查你的逻辑。

    回顾一下,在C和C++中,将一个32位整数移位32位,可能会产生未定义行为。使用make_clean,然后在编译代码时打开sanitizer(cmake -DCMAKE_BUILD_TYPE=RelASan)以便在你提交之前尝试捕捉你的代码中任何未定义的行为。

    你可以通过在build目录中运行./apps/network simulator来直接运行路由器测试。

  • 如果路由器没有到目的地的路由,或者TTL为零,它是不是应该向数据报的源头发送一个ICMP错误信息?

    在现实生活中,是的,这将是有帮助的。但在这个实验里没有必要——丢弃数据报就足够了。(即使在现实生活中,也不是每个路由器都会在这些情况下向源头发送ICMP消息)。

  • 我如何运行本实验的测试套件?

    make check_lab6(两个测试)。或者你可以用make check运行整个测试套件(161个测试)。

  • 如果这个PDF出来后还有更多的FAQ,我在哪里可以看到?

    请定期查看网站(https://cs144.github.io/lab_faq.html)和Piazza。

相关文章:

CS144-Lab6

概述 在本周的实验中&#xff0c;你将在现有的NetworkInterface基础上实现一个IP路由器&#xff0c;从而结束本课程。路由器有几个网络接口&#xff0c;可以在其中任何一个接口上接收互联网数据报。路由器的工作是根据路由表转发它得到的数据报&#xff1a;一个规则列表&#…...

最好的个人品牌策略是什么样的

在这个自我营销的时代&#xff0c;个人品牌越来越受到人们的重视。您的个人品牌的成功与否取决于您在专业领域拥有的知识&#xff0c;以及拥有将这些知识传达给其他用户的能力。如果人们认为您没有能力并且无法有效地分享有用的知识&#xff0c;那么您就很难获得关注并实现长远…...

第四届国际步态识别竞赛HID2023已经启动,欢迎报名

欢迎参加第四届HID 2023竞赛&#xff0c;证明您的实力&#xff0c;推动步态识别研究发展&#xff01;本次竞赛的亮点&#xff1a;总额人民币19,000元奖金&#xff1b;最新的SUSTech-Competition步态数据集&#xff1b;比上一届更充裕的准备时间&#xff1b;OpenGait开源程序帮您…...

「2」指针进阶——详解

&#x1f680;&#x1f680;&#x1f680;大家觉不错的话&#xff0c;就恳求大家点点关注&#xff0c;点点小爱心&#xff0c;指点指点&#x1f680;&#x1f680;&#x1f680; 目录 &#x1f430;指向函数指针数组的指针(很少用&#xff0c;了解) &#x1f430;回调函数&…...

计网笔记 网络层(端到端的服务)

第三章 网络层&#xff08;端到端的服务&#xff09; **TCP/IP体系中网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数据报服务。**网路层不提供服务质量的承诺&#xff0c;不保证分组交付的时限&#xff0c;所传送的分组可能出错、丢失、重复和失序。进程之间通信的…...

[蓝桥杯 2018 省 B] 日志统计——双指针算法

题目描述小明维护着一个程序员论坛。现在他收集了一份“点赞”日志&#xff0c;日志共有 N 行。其中每一行的格式是 ts id&#xff0c;表示在 ts 时刻编号 id 的帖子收到一个“赞”。现在小明想统计有哪些帖子曾经是“热帖”。如果一个帖子曾在任意一个长度为 DD 的时间段内收到…...

SpringMVC请求转发和重定向

请求转发&#xff1a;forward:重定向&#xff1a;redirect转发&#xff1a;由服务器的页面进行跳转&#xff0c;不需要客户端重新发送请求&#xff1a;特点如下&#xff1a;1、地址栏的请求不会发生变化&#xff0c;显示的还是第一次请求的地址2、请求的次数&#xff0c;有且仅…...

如何建立项目标准化评价体系?【锦狸】

PMO团队面临着管理多个项目&#xff0c;甚至是多个项目集&#xff0c;多个产品集的问题&#xff0c;那么如何对项目们进行标准化评价体系的建设&#xff0c;就是PMO需要首先思考的问题。 首先我们要关注项目的背景&#xff0c;了解了项目背景之后&#xff0c;我们才可以明确项…...

Vue基础入门讲义(二)-语法基础

文章目录1.vue入门案例1.1.HTML模板1.2.vue渲染1.3.双向绑定1.4.事件处理2.Vue实例2.1.创建Vue实例2.2.模板或元素2.3.数据2.4.方法3.生命周期钩子3.1.生命周期3.2.钩子函数3.3.this1.vue入门案例 1.1.HTML模板 在项目目录新建一个HTML文件 01-demo.html 1.2.vue渲染 01-d…...

应广单片机用8位乘法器实现16位乘法运算

应广单片机例如pms150&#xff0c;pms152这种是没有带乘法器的&#xff0c;如果需要进行乘法运算&#xff0c;可以用ide里面“程序产生器”菜单里面 产生乘法函数&#xff0c;把数据填入对应的参数&#xff0c;然后调用函数就可以实现乘法运算了。除此之外&#xff0c;应广还有…...

Android中使用GRPC简明教程

引言 Android作为一个开发平台&#xff0c;本身是使用java进行封装的&#xff0c;因此java可以调用的库&#xff0c;在Android中同样可以进行调用&#xff0c;这样就使得Android设备具有丰富的功能&#xff0c;可以进行各种类型的开发。 这篇文章就介绍如何在Android设备中使…...

【Linux】使用U盘自动化安装Linux(VMware虚拟机)

文章目录前言一、准备二、新建虚拟机2.1 创建虚拟机2.2 新增硬盘2.3 系统启动项三、加电运行四、EFI方式五、总结前言 一、准备 基于之前的基础【Linux】Kickstart 配置U盘自动化安装Linux系统&#xff0c;现在我们可以在虚拟机中尝试自动化安装Linux系统。 二、新建虚拟机 …...

内网渗透(五十七)之域控安全和跨域攻击-基于服务账户的非约束委派攻击

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…...

gitlab 安装到项目上传一篇解决

文章目录1.安装1.1创建挂载目录1.2启动1.3 配置gitlab查看docker admin 账户初始密码注册普通用户2.1进入注册2.2创建后通过登录admin审批3.2 步骤13.2 步骤23.3步骤33.4 项目添加成员4 使用成员用户,上传到新建的项目中4.1 复制项目地址4.2使用 git here 克隆项目4.3进入下载目…...

Verilog 逻辑与()、按位与()、逻辑或(||)、按位或(|)、等于(==)、全等(===)的区别

逻辑与&#xff08;&&&#xff09;逻辑与是一个双目运算符&#xff0c;当符号两边为1时输出1&#xff0c;符号两边为0时输出0。真值表&#xff1a;&&01xz00000101xxx0xxxz0xxx两个4bit的数字相与&#xff1b;A4b0x1z&#xff1b;B4b01xx&#xff1b;C4b00xz&am…...

剑指 Offer 22. 链表中倒数第k个节点

剑指 Offer 22. 链表中倒数第k个节点 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 输入一个链表&#xff0c;输出该链表中倒数第k个节点。为了符合大多数人的习惯&#xff0c;本题从1开始计数&#xff0c;即链表的尾节点是倒数第1个节点。 例如&#xff0c;一个链…...

数据结构预算法之买卖股票的最好时机(三)动态规划

目录&#xff1a;一.题目知识点&#xff1a;动态规划二.动态规划数组思路确定1.dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组一.题目知识点&#xff1a;动态规划动态规划算法的基本思想是&#xff1a;将待求解的问题分解成若干个相互联…...

【数通网络交换基础梳理2】三层设备、网关、ARP表、VLAN、路由表及跨网段路由下一跳转发原理

一、不同网段如何通讯 同网段可以依靠二层交换机通讯&#xff0c;网络中存在多个网段192.168.1.1/24 172.16.1.1/24 173.73.1.1/24情况下如何互相通讯&#xff1f;上节留一下的问题&#xff0c;这节继续讲解。 1、这里以Ping命令讲解&#xff0c;PC1 ping173.73.1.2&#xf…...

Java-排序链表问题

Java-排序链表问题题目题解方法&#xff1a;自顶向下归并排序算法题目 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 示例 2&#xff1a; 示例 3&#xff1a; 提示&#xff1a; *链表中节点的数目在范围 [0, 5 * 104]…...

c++之二叉树【进阶版】

前言 在c语言阶段的数据结构系列中已经学习过二叉树&#xff0c;但是这篇文章是二叉树的进阶版&#xff0c;因为首先就会讲到一种树形结构“二叉搜索树”&#xff0c;学习二叉搜索树的目标是为了更好的理解map和set的特性。二叉搜索树的特性就是左子树键值小于根&#xff0c;右…...

【数据库】 SQLServer

SQL Server 安装 配置 修改SQL Server默认的数据库文件保存路径_ 认识 master &#xff1a;是SQL Server中最重要的系统数据 库&#xff0c;存储SQL Server中的元数据。 Model&#xff1a;模板数据库&#xff0c;在创建新的数据库时&#xff0c;SQL Server 将会复制此数据…...

Linux 4.19 内核中 spinlock 概览

Linux内核中 spinlock相关数据结构和代码实现涉及的文件还是挺多的&#xff0c;这篇博客尝试从文件的角度来梳理一下 spinlock的相关数据结构和代码实现&#xff0c;适合想大概了解 Linux内核中 spinlock从上层 API到底层实现间的调用路径和传参变化&#xff0c;尤其适合了解 s…...

TensorFlow 1.x学习(系列二 :1):基本概念TensorFlow的基本介绍,图,会话,会话中的run(),placeholder(),常见的报错

目录1.基本介绍2.图的结构3.会话&#xff0c;会话的run方法4.placeholder5.返回值异常写在前边的话&#xff1a;之前发布过一个关于TensorFlow1.x的转载系列&#xff0c;自己将基本的TensorFlow操作敲了一遍&#xff0c;但是仍然有很多地方理解的不够深入。所以重开一个系列&am…...

javaEE 初阶 — 关于 IPv4、IPv6 协议、NAT(网络地址转换)、动态分配 IP 地址 的介绍

文章目录1. IPv42. IPv63. NAT4. 动态分配 IP 地址1. IPv4 在互联网的世界中只有 0 和1 &#xff0c;所以每个人都有一个由 0 和 1 组成的地址来让别人找到你。 这段由 0 和 1 组成的地址叫 IP 地址&#xff0c;这是互联网的基础资源&#xff0c;可以简单的理解为互联网的土地。…...

《Qt 6 C++开发指南》简介

我们编写的新书《Qt 6 C开发指南》在2月份终于正式发行销售了&#xff0c;这本书是对2018年5月出版的《Qt 5.9 C开发指南》的重磅升级。以下是本书前言的部分内容&#xff0c;算是对《Qt 6 C开发指南》的一个简介。1&#xff0e;编写本书的目的《Qt 5.9C开发指南》是我写的第一…...

CleanMyMac是什么清理软件?及使用教程

你知道CleanMyMac是什么吗&#xff1f;它的字面意思为“清理我的Mac”&#xff0c;作为软件&#xff0c;那就是一款Mac清理工具&#xff0c;Mac OS X 系统下知名系统清理软件&#xff0c;是数以万计的Mac用户的选择。它可以流畅地与系统性能相结合&#xff0c;只需简单的步骤就…...

Linux小黑板(9):共享内存

"My poor lost soul"上章花了不少的篇幅讲了讲基于管道((匿名、命名))技术实现的进程间通信。进程为什么需要通信&#xff1f;目的是为了完成进程间的"协同",提高处理数据的能力、优化业务逻辑的实现等等&#xff0c;在linux中我们已经谈过了一个通信的大类…...

Detr源码解读(mmdetection)

Detr源码解读(mmdetection) 1、原理简要介绍 整体流程&#xff1a; 在给定一张输入图像后&#xff0c;1&#xff09;特征向量提取&#xff1a; 首先经过ResNet提取图像的最后一层特征图F。注意此处仅仅用了一层特征图&#xff0c;是因为后续计算复杂度原因&#xff0c;另外&am…...

一个.Net Core开发的,撑起月6亿PV开源监控解决方案

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 项目发布后&#xff0c;对于我们程序员来说&#xff0c;项目还不是真正的结束&#xff0c;保证项目的稳定运行也是非常重要的&#xff0c;而对于服务器的监控&#xff0c;就是保证稳定运行的手段之一。对数据库、…...

C语言数据结构初阶(2)----顺序表

目录 1. 顺序表的概念及结构 2. 动态顺序表的接口实现 2.1 SLInit(SL* ps) 的实现 2.2 SLDestory(SL* ps) 的实现 2.3 SLPrint(SL* ps) 的实现 2.4 SLCheckCapacity(SL* ps) 的实现 2.5 SLPushBack(SL* ps, SLDataType x) 的实现 2.6 SLPopBack(SL* ps) 的实现 2.7 SLP…...