计算机网络基础 - 传输层和它的朋友
一、传输层
1.1 作用
- 给上层的应用层提供通信服务
- 明确数据传输的角色
- 网络层:通信两端是主机和主机,IP 数据报明确标志了两台主机 IP
- 传输层:通信两端分别是主机种的进程和进程,Socket 明确标志了 IP 和 端口
- 差错检测
- 复用:应用层所有的应用进程都可以通过传输层再传送到网络层
- 分用:传输层把网络层收到发送给应用进程的数据后,需要分别交付指明的各应用进程(端口)
1.2 服务器端口
1. 系统端口号
范围:0 ~ 1023
其中的常用端口:
应用程序 | FTP | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS |
---|---|---|---|---|---|---|---|
端口 | 21 | 23 | 25 | 53 | 69 | 80 | 443 |
2. 登记端口号
范围:1024 ~ 49151
1.3 客户端端口
范围:49152 ~ 65535
1.4 重点
-
端口和套接字?
-
无连接的 UDP 特点?
-
不可靠网络实现可靠传输的原理?
-
停止等待协议?
-
ARQ 协议?
二、UDP
2.1 概念
UDP,User Datagram Protocal,用户数据报协议
UDP 在 IP 数据报上增加了很少的功能,让传输层可以完成复用、分用、差错检测的功能
2.2 特点
- 无连接
- 尽最大努力交付,不保证可靠交付
- 面向报文,无论应用层传下来的报文多长,它都一次性交给网络层
- 没有拥塞控制
- 支持一对一、一对多、多对一、多对多通信
2.3 传输
UDP 传输不用 Socket,只需要用到端口号
从网络层到应用层:
传输层从网络层拿到 UDP 数据报,根据首部中的目的端口,把 UDP 数据报通过相应端口,上交给端口对应的应用进程
从应用层到网络层:
传输层从应用层拿到相应的报文数据,封装上 UDP 首部(包括源端口,目的端口等),再发送给相应的
2.4 改进
使用 UDP 时,针对其不可靠传输,可以进行适当改进,以减少数据丢失,在应用进程本身不影响的前提下的改进措施:
- 采用前向纠错
- 重传已丢失的报文
2.5 可靠传输
UDP 是不可靠传输的,那么如何实现可靠传输呢?
UDP 传输有以下几方面问题:
- 乱序 — 发送方无法确定丢失的是哪个数据包
- 差错 — 发送方和接受方无法确定出错的是哪个包
- 丢失 — 数据包丢失无法重发
参考 TCP 的可靠传输,主要是实现以下几个方面的机制:
- 字节编号 — 解决的是乱序问题
- 应答确认机制 — 解决的是检测数据包是否出现差错的问题
- 超时重传机制 — 解决的是数据包出错或丢失如何重发的问题
整体做法可以这样:
由于 UDP 在传输层是已经固定了,无法在传输层对它下手,那么就需要在应用层动手,实现上面说的三个基本机制,和传输层的 UDP 配合使用,老保证它的可靠传输
- 字节编号:在应用层,将报文分成数据包,给它们分别编号
- 应答确认机制:发送端发送的时候,初始化一个随机 x,给数据包编号 seq = x,如此编号,然后接收端接收的时候,把数据包存放到缓存里,然后给接收端发送一个 ACK 确认报文,表示收到,但缓存的数据包可以组成一个报文的时候,把数据包组合起来,取出报文数据
- 超时重传机制:发送端发送的时候,为每个发送包设置一个定时器,同时保存数据包的副本;当定时器超过时,发现还没收到该数据包的确认,就重发该数据包;若收到数据包的确认就删除相应的数据包副本
最后,现有的已经有类似的开源协议是能做到这些功能的了,甚至有些能做到流量控制和拥塞避免的
现在较为出名的开源的可靠传输应用层协议有 RUDP 和 UDT,它们配合 UDP 使用,支持高速广域网上的海量数据传输,而 TCP 在高带宽长距离网络上性能很差
三、TCP
3.1 概念
TCP,Transmission Control Protocal,传输控制协议
3.2 特点
- 面向连接
- 点对点通信,点指的是 Socket
- 可靠传输:无差错、不丢失、不重复、有序
- 全双工通信
- 面向字节流,应用层传下来的数据看作无结构的字节流
3.3 可靠传输
使用可靠传输协议来实现:
- 发送方:在出现差错时,重传出现差错的数据
- 接受方:在来不及处理收到的数据时,及时告诉发送方适当降低发送数据的速度
3.3.1 链路层
链路层的可靠传输通过停止等待协议实现,使用确认 + 重传实现,属于自动重传请求-ARQ 的一种
这是属于早期链路层保证可靠传输的协议,传输层是不使用的,原理如下:
1. 传输情况
- 无差错
- 有差错
分类讨论:
- 无差错
- A 发送分组 M1,发完暂停,等待 B 确认
- B 收到分组 M1,向 A 发送确认
- A 收到对 M1 的确认,继续发送 M2
- ......
- 有差错 1
- B 收到 M1 检测出差错,丢弃 M1,不通知 A 有错
- A 发现过了一段时间没有收到 M1 的确认,就重传 M1 分组
- ......
- 有差错 2
- B 收到 M1,向 A 发送确认,中途丢失
- A 发现过了一段时间没有收到 M1 的确认,就重传 M1 分组
- B 再次收到 M1,丢弃 M1,向 A 再次发送确认
- ......
总结一下,基本流程就是:
- A
- 发送
- 重传
- B
- 收到,发送确认
- 收到,有错不发送确认
- 收到,已有再次发送确认
2. 超时重传
每次发送完,设置一个超时计时器,计时器到期钱,收到对方的确认,撤销已设置的超时计时器
三个前提:
- 保留副本:发送方发送一个分组,必须暂时保留已发送的分组的副本,收到确认后才清除副本
- 分组编号:每个 “发送分组” 和每个 “确认分组” 必须编号,才能区分
- 时间设置:超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些
总结:
3. 提高信道利用率
通过流水线传输来提高信道利用率:
3.3.2 传输层
传输层的可靠传输,通过滑动窗口协议 + 超时重传来实现
1. 滑动窗口
TCP 滑动窗口以字节为单位,滑动窗口有三部分:前沿、主体、后沿部分,一般来说前沿和后沿是不断前移动,同时滑动窗口分为发送窗口和接收窗口
发送窗口的大小和序列号的开始是根据接收窗口的大小和期望的下一个序列号报文来确定的:
发送窗口前沿要移动,必须要后沿先移动,否则会超出范围:
接收窗口的确认只能从第一个接收到字节序号开始发送
例如图中,31 号卡翔了,它可能丢失了,或者滞留,此时 B 不会给 A 发确认,尽管 32、33 已经接收到了
直到 A 发现一直没有收到 31 号字节的确认,经过 2MSL 或其他时间,就会重传 31 号字节;另外,由于 A 可用窗口还没满,可用窗口内的字节也是可以一直发
B 虽然 31 号卡翔了,也不会影响它接受其他数据,只是它的窗口要前挪动必须要 31 号收到,发送了 31 号的确认给 A 了,才能继续发下一个
一句话就是 B 的数据能接收,但字节的确认要回发给 A 时,就看 B 窗口的第一个字节是否有卡翔
A 的数据能发,但后沿要往前挪动,就看 A 窗口的第一个字节是否已经接收到了,接受了才能前移
当 5 -16 图中的 31号字节解决了,A 与 B 窗口往前挪动可能情况如下:
31 号字节 B 又收到的时候(不管是 A 重传还是滞留的过来了),B 就可以回复 31、32、33 的确认了(参考 5-16 图的 B)
可以发现它又卡翔了!这个时候 B 是 34、35、36 没收到,而其他收到了字节,所以,又要等这三个字节过来了才能继续前进
2. 超时重传
除了滑动窗口,TCP 还使用超时重传来重传丢失或者错误的分组,而这里的重点是重传时间的选择
一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT,加权平均往返时间 RTTs 计算是:$RRTs = (1-a) * (RTTs) + a * RTT,其中,0 ≤ a < 1$
默认重传时间:$RTO = RTTs + 4 * RTT_d,其中,RTT_ 为偏差的加权平均值$
滑动窗口相当于一个缓存机制
而 TCP 的可靠传输 = 滑动窗口 + 字节编号 + 字节的确认机制 + 超时重传机制
3.4 拥塞控制
拥塞:某段时间,某对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能下降。直白地说资源需求大于供应就是拥塞。(因为需求方一直得不到响应)
流量控制针对的是点对点,两主机间的通信量控制;拥塞控制是针对全局的网络
拥塞控制通过四种算法:
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
3.5 连接管理
3.5.1 报文字段
- ACK:确认报文字段,只有为 1 时,该字段是有效的
- ack:确认号,期望收到的下一个报文段的序号
- FIN:终止报文字段,当 FIN=1时,此报文段发送方的数据已经发送完毕,请求释放连接
- SYN:同步序号,SYN=1,ACK=0 表示连接请求报文,SYN=1,ACK=1表示同意链接的响应报文
- seq:序号,对字节流进行编号,例如301,表示第一个字节的编号为 301
3.5.2 状态字段
三次握手字段
- LISTEN - 监听来自远方TCP端口的连接请求;
- SYN-SENT -在发送连接请求后等待匹配的连接请求;
- SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;
- ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;
四次挥手字段
- FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
- FIN-WAIT-2 - 从远程TCP等待连接中断请求;
- CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
- CLOSING -等待远程TCP对连接中断的确认;
- LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;
- TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认;
- CLOSED - 没有任何连接状态;
3.5.3 连接过程
书本图解
我的图解
3.5.4 释放过程
书本图解
我的图解
3.5.5 常见问题
TCP 连接管理 - 男人八问
1. 为何需要三次握手?
-
解决滞留连接问题
第三次握手是为了防止失效的连接请求,让服务器错误打开连接,这里主要针对的就是滞留的连接请求问题
-
滞留连接问题
客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回答连接确认,客户端等待一个超时重传时间之后,就会重新请求连接,但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接
-
三次握手解决滞留连接的过程
有了三次握手,客户端就会忽略服务器发送的对滞留连接请求的连接确认
那么忽略的具体过程是什么?
这里是通过第二次新的请求连接客户端发送给服务器的 ISN(初始化的 seq)不一样了,则服务器回复的SYN & ack 是针对第二次新的 seq,接着当客户端的滞留连接到达服务器,服务器再次发送 ack ,客户端又收到SYN & ack 了!但它会忽略此次的,因为是滞留的,不会继续连接,从而确定滞留的第一次的确认是无效的,因此,只要不进行第三次握手,就不会再次打开连接
2. 三次握手的作用是什么?
- 作用1:为了确认双方的接收与发送能力是否正常
- 作用2:指定自己的初始的序列号,为后面的可靠传输做准备
- 作用3:如果是 https 协议,三次握手时会进行数字证书验证以及加密密钥的生成等
3. 为什么只有三次握手才能确认,双方的接收发送能力正常,两次不可以?
- 第一次:由客户端发送,服务器收到,在服务器得到结论:客户端发送能力正常、服务器接收能力正常
- 第二次:由服务器发送,客户端收到,在客户端得到结论,服务器的接收、发送能力正常,客户端接收、发送能力正常,客户端连个确认已经完成,但服务器的确认还停留在第一次握手的认知状态(参考第一次)
- 第三次:由客户端发送,客户端收到,在服务器得到结论,客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常
4. 初始序列号 ISN 是固定的么?
ISN 就是初始化的 seq
三次握手的一个重要功能是客户端和服务器交换各自的 ISN 就是初始化的 ack,以便让对方知道接下来接收数据的时候如何按序列号组装数据
如果 ISN 是固定的,攻击者很容易猜出后续的确认号,所以 ISN 是动态生成的
5. 什么是半连接队列?
服务器第一次收到客户端的 SYN,就会处于 SYN-RCVD 状态,此时双方还没有完全建立其连接,服务器把处于此种状态下的请求连接放在一个队列里(这里指的是客户端有多个,跟同个客户端的滞留请求那个是不同的)把这种队列称之为半连接队列
6. 什么是全连接队列?
已经完成是那次握手,建立起连接后的连接就会放在全连接队列中,如果队列满了就会有可能出现丢包现象
7. 服务器确认请求重传如何确定的?
服务器发送了 SYN,ACK,如果未收到客户端确认包回复,服务器就会进行首次重传,等待一段时间仍未收到客户端确认包,就会进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统会将该连接请求信息从半连接队列中删除,重传等待时间不一定相同,一般是指数增长,例如间隔时间为 1s、2s、4s、8s......
8. 三次握手过程中可以携带数据么?为什么?
第一次、第二次握手不可以携带数据,第三次握手可以携带数据
-
第一次握手不携带的原因:
如果有人要攻击服务器,第一次握手的 SYN 报文中放入大量数据,不断重复发送 SYN 报文,这样会让 服务器花费很多时间、内存空间接收处理报文,所以第一次握手放入数据会让服务器更容易受到攻击
-
第三次握手携带的原因:
客户端已经处于 ESTABLISHED 状态,对于客户端来说已经建立起连接了,也知道服务器的接收发送能力了,所以携带数据回复服务器没问题
3.6 长短连接
Http 有长连接 & 短连接,底层还是根据联系 TCP 连接来实现的,联系如下:
-
Http 长连接:
C / S 一次 Http 操作,任务结束过一段时间再关闭,每次 Http 长连接可以更长时间利用一次 TCP 三次握手四次挥手的连接与释放
数据库用的是长连接,长连接多用于操作频繁,点对点的通讯,而且连接数不能太多
HTTP 1.1 默认是长连接,如果要使用短连接要使用:Connection : close
-
Http 短连接:
C / S 一次 Http 操作,任务结束就中断,每次 Http 短连接就要进行一次 TCP 三次握手四次挥手的连接与释放
WEB 网站访问响应用短连接,短连接用于访问量大,访问次数多的情况
HTTP 1.0 默认是短连接,如果要要使用长连接要使用:Connection : Keep-Alive
3.6 泛洪攻击
泛洪攻击指的是 DDoS 攻击的一种类型,就是分布式拒绝服务攻击,就是让正常用户无法正常地访问网站的服务
分布式拒绝服务器攻击有很多种,例如:
- SYN 泛洪
- ACK 泛洪
- UDP 泛洪
- DNS 泛洪
- HTTP 泛洪
- ICMP 泛洪
下面讲讲几种常见的:
SYN 泛洪攻击
利用 TCP 协议缺陷,发送大量伪造的 TCP 连接请求,从而使得被攻击方主机 CPU 满负载,内存爆满
这是因为,建立 TCP 连接,需要三次握手,SYN 泛洪就是大量的进行第一次握手后,就死机或者掉线,这样无法完成第三次握手,导致服务器会重试或者等待一段时间(至少 30 S)
服务器状态:当服务器维持数以万计的半连接 Socket,会耗费非常多的资源,导致服务器卡翔,无法响应
客户端状态:网站无法响应,无法访问
ACK 泛洪攻击
利用 TCP 协议的确认机制,发送大量的伪造 ACK 报文,从而使被攻击方主机负载变高,有效处理效率降低
因为连接建立后,双方通信的数据会通过 ACK 确认来判断差错,而判断 ACK 数据包前,主机会先去查该 ACK 对应的 TCP 连接是否存在,若不存在,就回应对方一个 RST 包,表示连接不存在。
总的来说,就是接收报文、判断状态、回应 RST 包,这几个操作是需要耗费时间的,ACK 泛洪就是使得被攻击方大量的重复做这几个操作
服务器状态:ACK 报文带来的负载比 SYN 报文要小的多,所以,需要大流量高速率的 ACK 小包冲击才会生效。当真正生效的时候,服务器没法及时处理正常用户的请求数据包,防火墙可能不堪重负瘫痪
客户端状态:访问网站页面慢,丢包率高
UDP 泛洪攻击
利用 UDP 协议的无连接,伪造大量的源 IP 地址去发送 UDP 数据包,正常情况下,UDP 双向流量基本相等,消耗对方的资源的同时,也在消耗自己的资源
DNS 查询泛洪攻击
利用 DNS 域名解析,向目标服务器发送大量的域名解析请求,而这些域名都是随机生成的无效域名,那么服务器解析大量的请求会引起域名解析服务器的高负载
服务器状态:无法及时解析正常请求的域名解析请求
客户端状态:域名解析超时,无法访问网站
3.7 常见问题
1.服务器出现大量的 TIME_WAIT ?
-
原因
其实现在的 HTTP 是 1.1 版本,默认就是长连接。比较少出现这种问题。一旦出现则说明存在大量的短连接,服务器主动发起四次挥手,而一个短连接关闭是要经过 2MSL 的时间的。那么大量的短连接会使得 Socket 大量处于 TIME_WAIT,累积起来就很大了
-
解决方案
-
对于客户端
针对现在的 HTTP 1.1,可以设置报文头部 Connection : Keep-Alive,设置短连接为长连接保持存活一段时间
-
对于服务器
可以缩减 TIME_WAIT 状态的时间,设置为 1MSL
允许 TIME_WAIT 状态的 Socket 重用,需要修改内核宏定义重新编译内核:
net.ipv4.tcp_tw_reuse = 1
加快 Socket 回收: net.ipv4.tcp_tw_recycle = 1
-
2.出现大量的 CLOSE_WAIT ?
-
原因
大量的 CLOSE_WAIT 是由于被动关闭引起的,原因是收到了 FIN 报文却没有回复 FIN-WAIT-1 报文的确认,这一定是应用程序引起的
-
解决
检查应用程序查看是否有错
3.出现大量的 SYN_RECEIVED ?
-
原因
可能是遭到了 SYN 泛洪攻击
-
解决
限制单个 IP 请求频率
使用负载均衡分流
采用 ISP 近源清洗
4.TIME_WAIT 作用?
TIME_WAIT 意思就是等待 2MSL 才 CLOSED 的状态
一是为了保证发送的最后一个 ACK 报文段能到达 B,若失败,可重传,再设置为 2MSL 即可
二是防止本次已失效的连接请求报文段出现在其他连接中,在这段时间让本次连接的报文能够全部从网络消失