【Linux网络】NAT技术、DNS系统、五种IO模型

发布于:2025-03-09 ⋅ 阅读:(17) ⋅ 点赞:(0)
头像
⭐️个人主页:@小羊
⭐️所属专栏:Linux
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述


1、NAT

NAT (网络地址转换)技术主要解决 IPv4 地址不够用的问题,是路由器的一个重要功能。它在网络通信过程中将一个IP地址空间的地址转换为另一个IP地址空间的地址,即私有地址和公有地址之间的转换。

NAT 转换过程:
在这里插入图片描述

  • NAT 路由器将源地址从 10.0.0.10 替换成全局的 IP 202.244.174.37
  • NAT 路由器收到外部的数据时,又会把目标 IP 从 202.244.174.37 替换回10.0.0.10
  • 在 NAT 路由器内部,有一张自动生成的,用于地址转换的表(NAPT)
  • 当 10.0.0.10 第一次向 163.221.120.9 发送数据时就会生成表中的映射关系

如果局域网内有多个主机都访问同一个外网服务器,那么对于服务器返回的数据中,目的 IP 都是相同的,因此NAPT需要用 IP+port 的方式来映射唯一的一台主机。

在这里插入图片描述

但是由于 NAT 依赖这个转换表,所以有诸多限制:

  • 无法从 NAT 外部向内部服务器建立连接
  • 装换表的生成和销毁都需要额外开销
  • 通信过程中一旦 NAT 设备异常,即使存在热备,所有的 TCP 连接也都会断开

2、代理服务器

代理服务器是一种中间服务器,充当客户端与互联网之间的桥梁。

| 工作过程:
当客户在浏览器中设置好代理服务器后,使用浏览器访问所有WWW站点的请求都不会直接发给目的主机,而是先发给代理服务器。代理服务器接受客户的请求后,会向目的主机发出请求,并接受目的主机的数据,存于代理服务器的硬盘中,然后再由代理服务器将客户要求的数据发给客户。如果代理服务器已经缓存了客户所需的数据,它会直接将这些数据返回给客户,而无需再次向目的主机请求。

头像

| 正向代理 & 反向代理:
正向代理是指客户端通过代理服务器来访问外部网络资源,即正向代理代理的是客户端,帮助客户端访问其无法直接访问的服务器资源;反向代理是指客户端不知道目标服务器的存在,代理服务器位于客户端和目标服务器之间,接收来自客户端的请求并将其转发给内部网络上的真实服务器进行处理,即反向代理代理的是服务器,将外部网络连接请求转发给内部网络上的服务器。


3、内网穿透

| 原理:
内网穿透的工作原理主要基于 NAT 技术的逆过程。NAT技术将内部网络的私有IP地址转换为外部网络的公网IP地址,实现内部网络与外部网络的通信。但是NAT设备会自动屏蔽非内网主机主动发起的连接,这使得从外网发往内网的数据包被NAT设备丢弃。内网穿透技术通过特定的方法,如端口映射、反向代理等,绕过这一限制,实现内外网之间的通信。

| 方法:

  1. 端口映射:通过在路由器上设置端口映射,将内网设备的某个端口映射到公网IP地址的某个端口上,从而实现外网访问内网设备的目的。这种方法需要路由器支持端口映射功能,并且需要知道内网设备的IP地址和端口号。
  2. 反向代理:在内网中部署一个反向代理服务器,该服务器将外网用户的请求转发给内网中的目标设备,并将目标设备的响应返回给外网用户。这种方法可以实现对外隐藏内网设备的真实IP地址和端口号,增加安全性。
  3. 内网穿透工具:使用专门的内网穿透工具,如Frp、NATOOL、花生壳等,这些工具通常提供图形化界面和命令行工具,简化配置过程。用户只需按照工具的提示进行配置,即可实现内网穿透。

| 应用:

  1. 远程办公:员工可以在家中或其他远程地点通过内网穿透技术访问公司内部网络中的资源和服务,实现远程办公。
  2. 家庭网络访问:用户可以通过内网穿透技术访问家庭网络中的NAS设备、智能家居设备等,实现远程管理和控制。
  3. API服务访问:开发者可以将内部网络中的API服务通过内网穿透技术暴露到公网上,供外部用户访问和使用。
  4. 游戏联机:在一些需要联机游戏的情况下,玩家可以通过内网穿透技术实现不同NAT设备之间的直接通信,提高游戏体验和稳定性。

注意事项: 内网穿透涉及到将内网设备暴露在外部网络环境中,存在一定的安全风险,要保证安全性、稳定性、兼容性、且要严格遵守法律法规。


4、DNS 和 ICMP

DNS 是一整套从域名映射到 IP 的系统。

TCP/IP 中使用 IP 地址和端口号来确定网络上的一台主机的一个程序,但是 IP 地址不
方便记忆,于是就有了一种叫主机名的东西,是一个字符串,并且使用 hosts 文件来描述主机名和 IP 地址的关系。

| 域名解析过程:

  1. 先查询浏览器缓存是否有该域名对应的IP地址。
  2. 如果浏览器缓存中没有,会去计算机本地的hosts文件中查询是否有对应的缓存。
  3. 如果host文件中也没有则会向本地的DNS服务器(比如中国移动)发送一个DNS查询请求。
  4. 如果本地DNS解析器有该域名的ip地址,就会直接返回,如果没有缓存该域名的解析记录,它会向根DNS服务器发出查询请求。根DNS服务器并不负责解析域名,但它能告诉本地DNS解析器应该向哪个顶级域(.com/.net/.org)的DNS服务器继续查询。
  5. 本地DNS解析器接着向指定的顶级域名DNS服务器发出查询请求。顶级域DNS服务器也不负责具体的域名解析,但它能告诉本地DNS解析器应该前往哪个权威DNS服务器查询下一步的信息。
  6. 本地DNS解析器最后向权威DNS服务器发送查询请求。 权威DNS服务器是负责存储特定域名和IP地址映射的服务器。当权威DNS服务器收到查询请求时,它会查找"example.com"域名对应的IP地址,并将结果返回给本地DNS解析器。
  7. 本地DNS解析器将收到的IP地址返回给浏览器,并且还会将域名解析结果缓存在本地,以便下次访问时更快地响应。
  8. 浏览器发起连接: 本地DNS解析器已经将IP地址返回给您的计算机,您的浏览器可以使用该IP地址与目标服务器建立连接,开始获取网页内容。

在这里插入图片描述

ICMP:
一个新搭建好的网络,往往需要先进行一个简单的测试,来验证网络是否畅通。但是 IP协议并不提供可靠传输,如果丢包了,IP 协议并不能通知传输层是否丢包以及丢包的原因。ICMP 协议是一个网络层协议,主要解决的就是这个问题。它的功能是:

  • 确认 IP 包是否成功到达目标地址
  • 通知在发送过程中 IP 包被丢弃的原因
  • ICMP 也是基于 IP 协议工作的,但是它并不是传输层的功能,因此人们仍然把它归结为网络层协议
  • ICMP 只能搭配 IPv4 使用,如果是 IPv6 的情况下,需要使用 ICMPv6

ping 命令基于 ICMP,是在网络层,而端口号是传输层的内容,在 ICMP 中根本就不关注端口号这样的信息。


5、五种IO模型

IO = 等 + 拷贝。

即在任何 IO 过程中,都包含两个步骤,第一是等待,第二是拷贝。拷贝效率受硬件的限制,不容易提高;而且在实际的应用场景中,等待消耗的时间往往都远远高于拷贝的时间,所以要想提高 IO 的效率,最核心的办法就是让等待的时间尽量少。

高效的IO就是:单位时间内,“等” 的时间比重越低,IO效率越高。

  1. 阻塞I/O

    • 特点:进程发起I/O操作后,全程阻塞,直到数据就绪并完成复制。阻塞 IO 是最常见的 IO 模型
    • 流程
      1. 应用调用recvfrom
      2. 内核等待数据到达(若未就绪,进程挂起)。
      3. 数据到达后,内核复制到用户空间。
      4. 应用处理数据。
    • 优缺点:实现简单,但资源利用率低,无法处理高并发。
  2. 非阻塞I/O

    • 特点:进程通过轮询检查I/O状态,数据未就绪时立即返回错误。
    • 流程
      1. 应用调用recvfrom,若数据未就绪,返回EWOULDBLOCK
      2. 应用轮询调用recvfrom,直到数据就绪。
      3. 数据就绪后,复制到用户空间(短暂阻塞)。
    • 优缺点:避免长期阻塞,但轮询消耗CPU,适用于低负载场景。
  3. I/O多路复用

    • 特点:使用select/poll/epoll监控多个描述符,任一就绪时通知应用。
    • 流程
      1. 应用调用select,阻塞等待描述符就绪。
      2. 当某个描述符就绪,select返回。
      3. 应用调用recvfrom读取数据(复制阶段可能阻塞)。
    • 优缺点:单线程处理多I/O,高并发下高效(如epoll),但编程复杂度较高。
  4. 信号驱动I/O

    • 特点:通过信号(如SIGIO)通知数据就绪,应用异步处理。
    • 流程
      1. 应用注册信号处理函数,开启信号驱动。
      2. 内核数据就绪时发送信号。
      3. 应用在信号处理函数中调用recvfrom读取数据(复制阶段可能短暂阻塞)。
    • 优缺点:减少轮询开销,但信号处理复杂,适用场景有限(如UDP)。
  5. 异步I/O

    • 特点:内核完成所有操作后通知应用,全程无阻塞。
    • 流程
      1. 应用调用aio_read,指定缓冲区并立即返回。
      2. 内核负责数据就绪和复制到用户空间。
      3. 完成后通过回调或信号通知应用。
    • 优缺点:高效且资源利用率高,但依赖操作系统支持(如Linux的io_uring或Windows的IOCP)。

理解这五种模型有助于根据需求选择合适的I/O策略,优化系统性能与资源利用率。

| 同步 vs 异步:

  • 同步I/O:前四种模型均需应用主动参与数据复制阶段(阻塞或短暂阻塞)。
  • 异步I/O:仅第五种模型由内核完全处理,应用无需等待任何阶段。只要参与了IO的过程,就是同步IO。

| fcntl:

  • 首先我们需要知道,文件描述符默认都是阻塞IO。

fcntl函数原型:

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

根据传入的 cmd 的不同,后面追加的参数也不同。
fcntl 函数有 5 种功能:

  • 复制一个现有的描述符(cmd=F_DUPFD
  • 获得/设置文件描述符标记(cmd=F_GETFDF_SETFD)
  • 获得/设置文件状态标记(cmd=F_GETFLF_SETFL)
  • 获得/设置异步 I/O 所有权(cmd=F_GETOWNF_SETOWN)
  • 获得/设置记录锁(cmd=F_GETLKF_SETLKF_SETLKW)

阻塞式IO:
在这里插入图片描述

我们可以用第三种功能将一个文件描述符设置为非阻塞:

#include <iostream>
#include <string>
#include <unistd.h>
#include <fcntl.h>

using namespace std;

void SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if (fl < 0)
    {
        perror("fcntl");
        return;
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

int main()
{
    string tips = "Please Enter# ";
    char buffer[1024];
    SetNonBlock(0);
    while (true)
    {
        write(0, tips.c_str(), tips.size());
        ssize_t n = read(0, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            cout << "echo# " << buffer << endl;
        }
        else if (n == 0)
        {
            cout << "read file end!" << endl;
            break;
        }
        else
        {
            cout << "read error: " << n << endl;
        }
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
从上面的测试中我们可以得出:

  • 文件描述符被设置为非阻塞,如果我们不输入数据,数据一直不就绪,则会返回错误-1

但是我们还知道,read函数读取错误也会返回-1,那怎么区分究竟是读取错误还是数据未就绪呢?通过errno查找更详细的错误原因。

在这里插入图片描述


本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像

网站公告

今日签到

点亮在社区的每一天
去签到