NAT(network address translation)技术说明
IP报文在转发的时候需要考虑 源IP地址 和 目的IP地址,
IP报文每到达一个节点,就会更改一次IP地址和目的IP地址,其中节点是指主机、服务器、路由器
那么这个更改是如何进行的呢?
除了运营商的路由器,到达其它类型的路由器都需要将当前IP报文中的srcIP改成该路由器对应的WAN口IP
上述的操作会造成一个结果:
如果一个IP报文经过了广域网才到达数据接收方,那么该数据接收方接收到的IP报文的源IP地址都是公网IP
也就是说,由于所有在广域网上传播的IP报文的源IP地址都是运营商路由器的WAN口IP
所以运营商路由器的WAN口IP属于公网IP
这也使得私有IP是不会在广域网中的IP报文出现的(包括源IP 和 目的IP)
每次IP报文在到达一个 路由器时 都要进行如下操作
m1. 更改IP报文的源IP地址
m2. 让IP报文的目的IP地址和子网掩码按位与,确定是否是当前子网内部的IP地址
m2.1. 如果m2判定为否,则传给上一级路由器,转到m1
m2.2. 如果m2判定为是,则传给当前同一网络号的其它路由器
m2 中的判断是借助路由表完成的,路由表形如下图
把目的IP和不同的子网掩码进行按位与,判断是否是和destination这个网络号相同,
对于例1,判断到第二行,发现相同
如果在检查到default行之前,都是不同的,则判定为否,比如例2,
则直接交给default行,Gateway列的那个位置的元素 192.168.10.1
一般一台主机都有两种类型的网络接口:1. eth0本地以太网 2. lo本地环回
数据链路层规定当前报文中有效载荷的长度最大长度(MTU)是1500字节
所以网络层的IP协议对于这个要求将会对上传输层传过来的一个很大的报文进行分片处理
到达接收方之后,还要在接收方的网络层处做组装操作
将一个报文进行分片,将会增加丢包的概率,因为一旦丢失其中的一片,就意味着整个报文丢失了
那么在传输层的TCP协议中设计的滑动窗口,需要进行分段发送,
每一段都不能太大,要小于MSS(最大段尺寸)起码保证到数据链路层的时候不会出现超过1500字节的情况
由于UDP协议的存在,如果非要分片,那么分片的操作是什么?
首先明确一点,一个服务器端可能收到多个客户端的请求,
其中有些客户端的网络层进行了分片操作,另一些没有
通过(m5. 16位标识), (m6. 3位标志), (m7. 13位片偏移) 这三者就能够管理分片操作,
(m5. 16位标识)在不同的IP报文中是不同的,但在不同片的IP报文中是相同的
(m6. 3位标志) 中有三位,其中
第一位暂时不用
第二位如果为1,表示当前的IP协议对待将来会超出MTU的IP报文会直接丢弃
第三位如果为0,表示当前的IP报文是经过IP分片得到的某一片报文,而且这片报文不是最后一个
第三位如果为1,表示当前的IP报文是经过IP分片得到的最后一片报文,或者根本就没有进行分片
(m7. 13位片偏移)将发送方网络层的IP报文进行拆分,拆分成IP报头和有效载荷 这两部分
对于其中有效载荷这一部分来说,
(m7. 13位片偏移)就是每一片报文发送的数据在这个有效载荷中所在的位置
由于只有13位,而IP协议中规定了最大报文长度是2 ^ 16次方,
也就是说从传输层中传过来的报文可以接近这个数字,
那么此时13位二进制数可能无法直接表示传输层要求发的内容的偏移量
那么IP协议对这13位数进行的处理如下:
首先,要求每个分片的切开的位置都是有效载荷都是x,其中这个x是8的倍数
其次,发送方传输层在知道一个分片的起始位置之后,对这个位置右移3位,放到这里
最后,接收方传输层在收到每一个分片后都对这个位置的数左移3位,
注意:一旦传输层穿过来的数据经 网络层检查,发现不满足数据链路层的要求
那么就会进行分片,就有可能产生只有一片报文中装着应用层报头,传输层报头的,
而其他片没有其他层报头的情况
那么IP报头必须要能够判断如下问题:
Q1: 接收方确定传过来的报文是分片的吗?
Q2: 如果接收方得到的IP报文是分片的,在传输过程中是否丢了某些片?
对于Q1,记接收方收到的IP报文是δi,判断下述条件是否成立
条件ξ:δi的(m6. 3位标志)的第三位是1 || ((δi的(m6. 3位标志)的第三位是0) && (δi的(m7. 13位片偏移) > 0))
ξ成立,需要进行组装;条件不成立,不需要组装
对于Q2,ξ成立时,将所有分片按照δ1,...,δn的(m7. 13位片偏移)排升序,判断下述条件是否成立
条件γ:∀1 <= i < n,都有δi的(m7. 13位片偏移) * 8 + δi的报文长度 == δi+1的(m7. 13位片偏移) * 8 成立
γ成立,报文的所有分片都传到了,γ不成立,至少有一个,下面这个例子非常
数据链路层中局域网内部的通信大多采用以太网
数据链路层传输的数据帧的示意图,以太网帧格式,遵循MAC协议
由于除了有效载荷之外的所有内容的所占字节数不变,
所以通以太网帧去掉前14个字节,和后4个字节就能得到有效载荷,也就实现了报头和有效载荷的解包
考虑包括多个主机的以太网,
规定任何时刻都只能有一台主机的数据帧到这个局域网的传输通道中存在,而且在这个数据帧传输时,
所有局域网中的主机都会检查这个数据帧的目的MAC地址是否和自己数据链路层的MAC地址相同
也就是这个数据帧将会被所有主机的底层看到
交换机的功能:将局域网这个打的碰撞域分成多个小的碰撞域
考虑主机A 发送 数据帧给主机B,会有以下的问题出现
问题:如果主机A 和 主机B 的IP地址不在同一个网段,那么主机A在传输时是无法获得对方局域网内的某个地址的。 也就无法获得主机B的MAC地址,
但是由于数据链路层需要用MAC地址来确定当前的数据帧是否到达了目标主机,
那么到达主机B的局域网之后,需要用某种办法来解决这个问题。
办法:通过IP地址来形成MAC地址,保证到达目标主机的子网之后,还能准确在这个子网中找到目标主机
这种用IP地址来形成MAC地址的协议称为 ARP协议
注意:m1. 数据链路层实际上有两种协议:ARP协议和 MAC协议
m2. ARP协议的结果并不会交给上层,也就是ARP协议只会被MAC协议看到,不会被其他层的协议看到
主机A发送的数据帧δ已经来到了主机B所在局域网N对应的路由器,到达主机B之后将会执行如下操作
m1. 广播,路由器把δ传给N中的所有主机,只有N中IP地址和δ相同的那个地址会进行响应
m2. 通信
对于任意一台主机,
有可能会发送ARP请求给其他主机,然后收到ARP应答,
也有可能会收到来自其他主机的ARP请求
那么对于这台主机来说,怎么区分收到的是 ARP请求 还是 ARP应答?
考虑主机A 发送 数据帧给主机B,且局域网中还有主机C,ARP协议的具体操作过程:
s1: 主机A将
m1. 源IP:主机A的IP
m2. 源MAC地址:主机A的MAC地址
m3. 目的IP地址:主机B的IP地址
m4. 目的MAC地址:FFFFFFFFFFFF
m5. oper位:表明这个报文是一个ARP请求的报文
这些信息形成一个ARP报文,传给MAC层
s2: 在A主机的MAC层,在ARP报文的前面再添加报头,填写如下内容
n1. 目的MAC:FFFFFFFFFFFF(广播地址)
n2. 源MAC:本主机的MAC地址
n3. 帧类型为0806,表示这个帧是带有ARP报文的,
n4. CRC
s3: 主机B 和 主机C 都会收到S2中添加了ARP和MAC报头的报文δ,
每个主机在数据链路层看到δ之后,知道帧类型是0806,都会执行下面的操作
s3.1 主机B查看δ的ARP报文中(m5. oper位)是ARP应答位
s3.1.1 那么再看自己的IP 是不是和(m3. 目的IP地址)相同
如果不相同,则直接丢弃δ(比如主机C)
如果相同,准备对δ写应答(比如主机B)
s4: 主机B在得到δ之后,将填写新的ARP报文β,其中各项为:
m1. 源IP:主机B的IP地址
m2. 源MAC地址:主机B的MAC地址
m3. 目的IP地址:主机A的IP
m4. 目的MAC地址:主机A的MAC地址
m5. oper位:表明这个报文是一个ARP应答的报文
然后将β传到局域网中
s5: 在B主机的MAC层,在ARP报文的前面再添加报头,填写如下内容
n1. 目的MAC:主机A的MAC地址
n2. 源MAC:本主机的MAC地址
n3. 帧类型为0806,表示这个帧是带有ARP报文的,
n4. CRC
s5: 局域网中所有除了主机A的主机,都会看到β,然后在MAC层丢弃
主机A看到β后,现在MAC层看到帧类型是0806,传给ARP层
ARP层看到oper是ARP应答
s5.1 主机A查看β的ARP报文中(m5. oper位)是ARP应答位
s5.1.1 主机A将会发送真正的数据帧α,将α发往主机B
s6: α将不会有ARP层的报文,主机A将在α写下:
n1. 目的MAC:主机B的MAC地址
n2. 源MAC:本主机的MAC地址
n3. 帧类型为0800,表示这个帧不是带有ARP报文,
n4. CRC
s7. 主机A和B正常通信
每个主机的都会让ARP把当前的MAC地址和 IP地址之间的映射关系记录下来
可以在控制台输入 arp -a 命令查看这个映射关系
引入NAT转化表,就可以从公网获取报文到内网中
这是因为这个表标明了一个报文的源地址在到达运营商路由器前后的映射关系
即转换前的源IP地址(LAN口的地址) <----> 转换后的IP 地址(WAN口的地址)
注意:
m1. 端口号也要进行映射,
比如下图中的场景,两个报文α和β要到达的目的IP相同
发出α的主机 和 发出β的主机在同一个局域网,那么此时只有通过改变端口号,
以确保双向的映射都是唯一的
m2. 传输过程中经过的每一个路由器 都要维护一张NAT转化表
内网穿透:
A主机和B服务器建立好连接之后,中途的所有路由器都会这个连接维护NAT表
C主机就可以通过访问B服务器来访问A主机,
首先要在B服务器上部署一种 服务,部署的工作可以通过frp这个软件来实现
NAT和代理服务器的区别有哪些?
网络通信的本质就是进程间通信
进程间通信的本质就是 IO
站在进程的角度理解IO,输入输出实际上分别对应:
将数据从磁盘中刷新到内存,将数据从内存刷新到磁盘
IO应该如何理解?
刷新就是将内容进行拷贝,如果不满足条件,就进入阻塞等待状态
什么叫做高效的IO?
拷贝非常快,但是拷贝需要的条件满足的越晚,则等待的时间越长,效率越低。
所以需要减少等的比重来提高效率
五种IO模型:
m1. 阻塞IO
m2. 非阻塞IO IO不等待,但是会反复询问资源是否就绪
由于每次询问都会消耗CPU的资源,所以这种模型可能会导致CPU资源迅速被耗尽
m3. 信号驱动IO 如果资源准备就绪了,就会发送信号,通知可以进行IO
m4. 多路复用IO 向多个资源进行申请,一旦有一个资源就绪,就进行IO
m5. 异步IO 进程把等待资源的过程交给内核,直到资源准备好,内核就进行IO,然后进程再处理资源
前4种称为同步IO,第5种称为异步IO
同步IO:需要参与IO的过程
异步IO:无需参与IO的过程
非阻塞IO
一个文件描述符,可以设置其等待状态,默认的等待状态是阻塞IO
可以通过fcntl 这个函数新增一个 文件描述符 对应的 默认的等待状态
int fcntl(int fd, int cmd, ... /* args */); 具体的使用样例如下:
键盘输入内容之后在显示器中显示的示意如下,其中输入输出缓冲区是在 OS中存放的,
回显 是进行了如下操作之后的结果
s1: 用户通过键盘键入数据,
s2: 操作系统把这些数据读取到 输入缓冲区中,
s3: 操作系统再把这些数据给输出缓冲区拷贝一份
s4: 操作系统将输出缓冲区的内容刷新到显示器,显示器展示用户键入的数据
回显功能可以被关掉,也就是s3, s4没有进行
输入 ctrl + d 表明键盘输入结束
多路转接 select
为了等待多个fd,等该fd上面的新事件就绪,通知程序员,事件已经就绪,就可以进行IO拷贝了
select是一种多路转接方案
select只负责"等","等"的含义是
同时等待多个fd,只要有一个文件描述符说明当前已经等待就绪了
就会通知程序员,事件已经准备就绪了,之后的拷贝工作还是需要其他接口进行
select作为一种系统调用接口,其声明为:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);