OSI 七层网络模型称为开放式系统互联参考模型,是一种逻辑上的定义和规范。把网络从逻辑上分为了 7 层,每一层都有相关、相对应的物理设备,如路由器、交换机等。
分层名称 | 功能 | 每层功能概览 | 协议 | |
---|---|---|---|---|
7 | 应用层 | 针对特定应用的协议 | 文件传输、电子邮件、文件服务、虚拟终端 | TFTP、HTTP、FTP、DNS、Telnet |
6 | 表示层 | 设备固有数据格式和网络标准格式的转换 | 数据格式化、代码转换、数据加密 | 没有协议 |
5 | 会话层 | 通信管理。负责建立和断开通信连接(数据流动的逻辑通路) | 何时建立连接,何时断开连接以及保持多久的连接? | 没有协议 |
4 | 传输层 | 管理两个节点之间的数据传输,负责可靠传输 | 是否有数据丢失 | TCP、UDP |
3 | 网络层 | 地址管理和路由选择 | 经过哪个路由器传递到目的地址? | IP、ICMP、RIP、BCP |
2 | 数据链路层 | 互联设备之间传送和识别数据帧,错误检测 | 数据帧与比特流之间的转换 | SLIP、ARP、RARP |
1 | 物理层 | 以二进制数据形式在物理媒体上传输数据 | 比特流和电子信号之间的转换 | IS02110,IEEE802 |
- 对于一台主机,它的操作系统内核实现了从传输层到物理层的内容
- 对于一台路由器,它实现了从网络层到物理层
- 对于一台交换机,它实现了从数据链路层到物理层
- 对于集线器,它只实现了物理层
从上图的对比图可看出,TCP/IP 与 OSI 在分层模块上稍有区别。OSI 参考模型注重“通信协议必要的功能是什么”,而TCP/IP 则更强调“在计算机上实现协议应该开发哪种程序”。
UDP 的特点有 无连接、不可靠、面向数据报
UDP 传输的无连接特性体现在,在传输过程中只要知道对端的 ip 和端口号就可以直接进行传输,不需要建立连接
使用 udp 发送数据时,只确保发送信息的大小,却不能保证消息一定会到达。
udp 没有确认机制、没有重传机制。
而且就算因为网络故障该段无法发送到对方,UDP 协议层也不会给应用层返回任何错误信息。
应用层交给 udp 多长的报文,UDP 原样发送,既不会拆分,也不会合并。
例如:用 UDP 传输 100 个字节的数据。
如果发送端调用一次 sendto 发送 10 个字节,那么接收端也必须调用对应的一次 recvfrom 接收 100 个字节;而不是循环调用 10 次 recvfrom ,,每次接收 10 个字节。
UDP 是面向数据报这一特点,是UDP 的缓冲区决定的。
UDP 用于对高速传输和实时性较高的通信领域,例如:早期的QQ、视频传输等,还可用于广播。
TCP 的特性:面向连接、可靠、面向数据流
TCP 相对于 UDP 来说比较复杂,因为既然保证可靠性,同时又要尽可能的提高性能。
保证可靠性的机制有:
- 校验和 :在 TCP 的首部,16位
- 序列号 / 确认序列号
- 确认应答
- 超时重传
- 连接管理
- 流量控制
- 拥塞控制
提高性能的机制有:
- 滑动窗口
- 快速重传
- 延迟应答
- 捎带应答
什么是可靠性问题:
发送方发送的TCP数据的头部带有序列号,接收方如果接收到这段数据,就会发送一个 ACK + 确认序列号 的响应报文来确认应答,在发送方收到来自接收方的响应报文时,就明白这段数据发送成功。
ACK + 确认序列号的意思是告诉发送方:我已经收到了,下一次你从哪里(ASN)开始发
基于刚才的例子,如果发送方过了一段特定的时间并没有收到来自接收方的确认应答,就会重新发送刚才的数据。
造成这一现象的原因都两个:
注意:这里的特定时间随着网络情况的不同是有差异的,如果时间太长,会影响整体的重传效率,如果时间太短,则有可能会频繁的发送重复的包。在 Linux 和 Windows 中,超过 500ms 为一个单位进行控制,每次判定超时重传的超级时间都是 500ms 的整数倍,且以指数形式增长。累计到一定的重传次数,如果仍然没有确定应答返回,TCP 认为网络或者对端主机出现异常,强制关闭连接。
第一次握手:建立连接,客户端发送 SYN=1 的同步报文段到服务器,同时从CLOSED状态进入到SYN_SENT 状态,等到服务器确认;
第二次握手:服务器收到来自客户端的同步请求(带有 SYN 的数据报),并确认客户的 SYN,同时自己也发送一个带有 SYN 的确认报文,即SYN+ACK包,此时服务器从LISTEN 状态进入 SYN_RECV 状态;
第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK,此包发送完毕后,客户端进入ESTABLISHED 状态,在服务器接收到包后,也进入 ESTABLISHED 状态,完成三次握手过程。
四次挥手:
由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向上的连接。收到一个 FIN 意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的以访将执行主动关闭,而另一方就是被动关闭方。
假设客户端为主动关闭方,则服务器为被动关闭方。
第一次挥手:客户端发送一个带有 FIN 的结束报文段,用来关闭到服务器的数据传送,此时客户端从ESTABLISHED 状态进入 FIN_WAIT_1 状态;
第二次挥手:服务器收到来自客户端的关闭请求,它发回一个 ACK 响应,同时从 ESTABLISHED 状态变为 CLOSE_WAIT 状态,在客户端收到来自服务器的响应后,也从 FIN_WAIT_1 进入 FIN_WAIT_2 状态;
第三次挥手:被动关闭方服务器也完成自己的数据传输,向客户端发送一个带有 FIN 的请求,从 CLOSE_WAIT 状态进入 LAST_ACK 状态;
第四次挥手:客户端给服务器发回 ACK 响应,同时从 FIN_WAIT_2 状态进入 TIME_CLOSE 状态,在服务器收到响应后,服务器进入 CLOSE 状态,客户端等待 2MSL 时间后也进入 CLOSE 状态。
为什么是三次握手而不是两次?
首先为了防止网络攻击,每个连接两端的初始序列号都是随机的,所以需要一开始双方先沟通,确认对方的序列号。在服务器对客户端的请求进行回应(也就是第二次握手的时候)后,服务器就会认为连接已经建立,而如果客户端没有收到服务器端的响应,此时客户端仍然认为连接没有建立,服务器会对已经建立连接的资源进行保存,如果出现大量的这种情况,服务器会崩溃。
为什么是四次挥手还不是三次?
这是因为服务器端的 LISTEN 状态下的 socket 当收到 SYN 同步报文的请求后,它可以把 ACK 和 SYN 放在一个报文中发送。但是关闭连接时,当被动关闭方收到来自主动关闭方的 FIN 结束报文段时,可能还有数据没有发送完毕,所以被动关闭方未必会马上关闭 socket,即被动关闭方在发送一些数据后,再发送 FIN 结束报文段给对方表示你现在可以关闭连接了。所以第二次挥手的 ACK 和 第三次发送的 FIN 多数情况下都是分开发送的。
为什么 TIME_WAIT 的时间是 2MSL?
MSL 是TCP 报文的最大生存时间,因此如果 TIME_WAIT 状态如果持续存在 2MSL 的话:
出现大量的CLOSE_WAIT状态
CLOSE_WAIT 状态出现在被动关闭方,此时主动关闭方已不再向被动关闭方发送数据,但被动关闭方还没有关闭连接,如果服务上出现大量的 CLOSE_WAIT 状态,原因就是服务器没有正确关闭 socket,导致四次挥手没有完成导致的。这是一个 BUG,需要加上对应的 close 即可解决。
TCP 以 1 个段位单位,每发送一个段进行一次确认应答的处理。这样有一个缺点,就是包的往返时间越长通信性能就越低,且一个包一个确认应答太浪费时间,效率太低。
为解决这个问题,TCP 引入了滑动窗口的概念。确认应答机制不再是以每个分段,而是以更大的单位进行确认,转发时间将会大幅的缩短。也就是说,发送端发送了一个段后,不必一直等待确认应答而是继续发送。
假设一窗口大小为4000个字节,每次发送1000个字节即为 4 个段,在发送方发送前 4 个段的时候不需要等待任何 ACK,直接发送。收到第一个 ACK 的时候,继续发送第 5 个段的数据,依次类推。
操作系统为了维护这个滑动窗口,需要开辟发送缓冲区来记录还有哪些数据没有应答,只有确认过的数据才能从缓冲区删掉。
万一造成丢包:
情况1:部分ACK 丢了,可以通过后续的 ACK 确认,接收方并不会一直发送同一 ACK 响应,可以根据发送的最后一个响应判断得知,接收方接收到了所有的数据,只是ACK 没有收到而已,所以不需要再次发送。
情况2:数据包直接丢了,接收端没有接收到来自中间的一部分数据,就会一直发 这段的 ACK。比如发送 4 个报文段,1001- 2000,2001 - 3000,3001 - 4000,4001 - 5000,接收方连续收到 3 个“1001”这样的应答,就会将对应的数据 1001 ~ 2000 重新发送。这时接收端接收到 1001 后,返回的 ACK 就是 5001,因为其他的数据,接收端其实已经收到了,被放在了操作系统内核的接收缓冲区。这种机制也叫做快重传。
想要了解流量控制,就先得说一下滑动窗口的概念。
接收端处理数据的能力是有限的,如果发送端发送的太快,导致接收端的缓冲区被打满,这时发送端继续发送数据就会造成丢包,因而引起丢包重传等一系列反应。
因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度,这个机制就叫做流量控制。
实现过程是接收方每次收到数据包,在发送确认报文的时候,同时通过 TCP 首部的窗口大小这一字段,告诉发送方自己的缓存区还有多少是空闲的,窗口字段越大,说明网络的吞吐量越高。接收端一旦发现自己的缓冲区快满了,接收方就会将窗口大小设置为一个更小的值;发送端接收到这个窗口后,就会减慢自己的发送速度。如果缓冲区满了,就会将窗口大小的值设置为 0,这时发送方不再发送数据,但需要定期发送一个窗口探测数据段,使接收方把窗口大小告诉发送端。
虽然 TCP 有了滑动窗口能够保证 TCP 的性能,但也要注意它的可靠性。如果在刚开始不清楚当前网络状况下,直接发送大量的数据就会引发问题。
TCP 引入慢启动机制,即先发送少量数据,探探路,摸清当前网络拥堵状况后,再决定按照多大的速度传输数据。
在 TCP 刚启动的时候,窗口大小为1,收到响应后,将窗口大小设为 2,这个过程窗口的大小按照指数增长,即第 3 次设为4,第四次设为8。一旦窗口大小超过慢启动的阈值,不再按照指数方式增长,而是按照线性方式增长。
在发生网络拥塞时,就将窗口大小重新设为1,慢启动的阈值设为发生网络拥塞时窗口大小的一半。
少量的丢包,仅仅是触发超时重传;大量的丢包,就认为是网络拥塞。
TCP 的拥塞控制,归根结底就是TCP 想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的这种方案。
延迟应答机制是提高 TCP 性能的一个机制。
假设当前接收端缓冲区为 1M,一次接受了 500K 的数据;如果立刻应答,返回的窗口大小为 500K,但实际上可能处理端处理的速度很快,10ms 内就把 500K 的数据从缓冲区消费掉了,在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再大点,也能处理过了。如果接收端稍微等一会,比如等待 200 ms再应答,那么这个时候返回的窗口大小就是 1M。
一般情况下:
一般N = 2,最大延迟时间 = 200ms
创建一个 TCP 的socket,同时会在系统内核创建一个 发送缓冲区和一个 接收缓冲区:
首先要声明,粘包中的“包”指的是应用层的数据包。在 TCP 的协议头中,没有像UDP 一样的“报文长度”这样的字段。站在传输层的角度,TCP 是一个一个报文传输过来的,然后按照序号排列好放在缓冲区中;站在应用层的角度,看到的只是一串连续的字节数据。那么应用程序看到这么一串字节数据,并不知道从哪个部分开始到哪个部分,才是一个完整的应用层数据包。
如何避免粘包问题呢?归根结底:明确两个包之间的边界
造成粘包的原因
UDP 会造成粘包吗?
不会。对于UDP来说是一个一个把数据交付给应用层,有很明确的数据边界。站在应用层的角度,使用UDP 的时候,要么收到完整的 UDP 报文,不会出现“半个”的情况。
进程终止:进程终止会释放文件描述符,仍然可以发送FIN,与正常关闭一样;
机器重启:和进程终止的情况相同;
机器断电 / 网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端会发现连接已经不在了,就会进行 reset,发送复位报文段。即使没有写入操作,TCP 自己也内置了一个保活定时器,会定期访问对方是否还在。如果不在就释放连接。
另外,应用层的某些协议,也有这样的检测机制。例如 HTTP 长连接中,也会定期检测对方的状态。例如QQ,在QQ掉线之后,也会尝试重新连接。
SYN 攻击利用TCP协议的缺陷,通过发送大量的半连接请求,耗费CPU和内存资源。SYN 攻击除了能影响主机外,还可以危害路由器、防火墙等网络系统,事实上SYN攻击并不管目标是什么系统,只要打开TCP 服务器就可以实施。
当服务器收到连接请求,将此信息加入到未连接队列,并发送请求给客户端,此时进入 SYN_RECV 状态,当服务器未收到客户端的确认包时,重新发送请求包,一直到超时,才将此条目从未连接队列删除。
配合 IP 欺骗,SYN攻击能达到很好的效果。通常客户端在短时间内伪造大量不存在的 IP 地址,向服务器不断地发送 SYN 包,服务器回复确认包,并等待客户的确认,由于源地址不存在,服务器会不断的重发直至超时。这些伪造的 SYN 包将长时间占用未连接队列,正常的 SYN 请求被丢弃,目标系统运行缓慢,严重的困难会引起网络拥塞甚至瘫痪。
重传定时器使用于当希望收到另一端的确认;
坚持定时器使窗口大小信息保持不断流动,用于流量控制;
保活定时器可检测一个空闲连接的另一端是否崩溃或者是否重启;
2MSL定时器测量一个处于TIME_WAIT 状态连接的时间。
校验和:位于 TCP 首部,占据 16 个字节,判断发送数据的正确性,如果校验失败,直接丢弃;
序列号:TCP 给每个字节的数据都进行了编号,确保收到的数据和发送端发送的数据的序列一致,且能够保证收到的数据不重复,根据序列号,将重复的数据直接丢弃,实现去重效果;
确认应答机制和超时重传机制:发送端发送数据后,若接收端接收到数据,要发送确认应答的响应。在特定的时间内,未收到确认应答,则重新发送原数据;
连接管理:发送数据前进行三次握手,发送完数据进行四次挥手;
流量控制:滑动窗口和计时器的使用。TCP 的首部窗口大小会指明当前接收端能够接收的最大数据量,发送方确保不会发生由于发送方发送报文太快,接收方无法及时处理而导致的丢包问题。当窗口大小为0时,发送方不再发送数据,但需发送窗口探测数据段,查看接收方是否可以接收数据了;
拥塞控制:TCP 的拥塞控制由 4 个核心算法组成:慢启动、拥塞避免、快重传、快恢复。
IP 地址分为两个部分,网络号和主机号
手动管理子网内的IP 是一件相当麻烦的事情,DHCP 技术能够自动的给子网内新增的主机分配 IP 地址,避免了手动管理 IP 的不变。一般的路由器都带有DHCP 功能,因此路由器也可以看作是一个 DHCP 服务器。
DHCP 这种技术会造成 IP 地址浪费,针对这种情况提出了 CIDR 技术
例如:
IP 地址 | 140.252.20.68 |
---|---|
子网掩码 | 255.255.255.240 |
网络号 | 140.252.20.64 |
子网地址范围 | 140.255.20.64 ~ 140.252.20.79 |
如果一个组织内部组建局域网,IP 地址只用于局域网通信,因此它不直接连到 Internet 上。