传输层协议
- TCP, 传输控制协议
- UDP,用户数据报协议 无连接,尽最大努力交付
TCP
- TCP 提供一种面向连接的、可靠的字节流服务
- 在一个 TCP 连接中,仅有两方进行彼此通信。广播和多播不能用于 TCP
- TCP 使用校验和,确认和重传机制来保证可靠传输
- TCP 给数据分节进行排序,并使用累积确认保证数据的顺序不变和非重复
- TCP 使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制
1 | 注意:TCP 并不能保证数据一定会被对方接收到,因为这是不可能的。TCP 能够做到的是,如果有可能,就把数据递 |
TCP连接
每一条TCP连接唯一地被通信两端的两个端点(即两个套接字)所确定
TCP连接 :: = { socket1,socket2} = {(IP1 : port1),(IP2 : port2)}
IP1和IP2是两个端点的IP地址,port1和port2是两个主机应用的端口号,TCP连接的端点是套接字。
数据传输开始之前,需要建立连接(三次握手)。 数据传输结束之后,需要释放连接(四次挥手)
TCP可靠传输的精髓
TCP连接的一方A,由操作系统动态随机选取一个32位长的序列号(Initial Sequence Number),假设A的初始序列号为1000,以该序列号为原点,对自己将要发送的每个字节的数据进行编号,1001,1002,1003…,并把自己的初始序列号ISN告诉B,让B有一个思想准备,什么样编号的数据是合法的,什么编号是非法的,比如编号900就是非法的,同时B还可以对A每一个编号的字节数据进行确认。如果A收到B确认编号为2001,则意味着字节编号为1001-2000,共1000个字节已经安全到达。
同理B也是类似的操作,假设B的初始序列号ISN为2000,以该序列号为原点,对自己将要发送的每个字节的数据进行编号,2001,2002,2003…,并把自己的初始序列号ISN告诉A,以便A可以确认B发送的每一个字节。如果B收到A确认编号为4001,则意味着字节编号为2001-4000,共2000个字节已经安全到达。
TCP连接握手,握的是啥?通信双方数据原点的序列号!
三次握手
1 | SYN(synchronous建立联机) |
建立一个 TCP 连接时,需要客户端和服务器总共发送3个包.
三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。
在 socket 编程中,客户端执行 connect() 时。将触发三次握手。
第一次握手(SYN=1, seq=x)
由客户端向服务端发送 SYN 标志位置1的包 ,指明客户端打算连接的服务器的端口,以及初始序号 X。第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
为什么是”三次握手”?
两次握手的过程:
2.1 A 发送同步信号SYN + A’s ISN(Initial sequence number)
2.2 B发送同步信号SYN+B’s ISN+B‘s ACK sequence number
这里有一个问题,A与B就A的初始序列号达成了一致,这里是1000。但是B无法知道A是否已经接收到自己的同步信号,如果这个同步信号丢失了,A和B就B的初始序列号将无法达成一致
- 如果A发给B的确认丢了,该如何?
A会超时重传这个ACK吗?不会!TCP不会为没有数据的ACK超时重传。B如果没有收到A的ACK,会超时重传自己的SYN同步信号,一直到收到A的ACK为止(SYN攻击埋下伏笔)。
SYN攻击
在三次握手过程中,服务器发送 SYN-ACK 之后,收到客户端的 ACK 之前的 TCP 连接称为半连接(half-open connect)。此时服务器处于 SYN_RCVD 状态。当收到 ACK 后,服务器才能转入 ESTABLISHED 状态.
SYN 攻击指的是,攻击客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送SYN包,服务器回复确认包,并等待客户的确认。由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,导致目标系统运行缓慢,严重者会引起网络堵塞甚至系统瘫痪。
SYN 攻击是一种典型的 DoS/DDoS 攻击。
- 如何检测 SYN 攻击?
检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。
- 如何防御 SYN 攻击?
SYN攻击不能完全被阻止,除非将TCP协议重新设计。我们所做的是尽可能的减轻SYN攻击的危害,常见的防御 SYN 攻击的方法有如下几种:
缩短超时(SYN Timeout)时间
增加最大半连接数
过滤网关防护
SYN cookies技术
四次挥手
TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。
为什么要”四次挥手”
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭
TCP协议的连接是全双工连接,一个TCP连接存在双向的读写通道。
简单说来是 “先关读,后关写”,一共需要四个阶段。以客户机发起关闭连接为例:
1.服务器读通道关闭
2.客户机写通道关闭
3.客户机读通道关闭
4.服务器写通道关闭
- 挥手过程
第一阶段 客户机发送完数据之后,向服务器发送一个FIN数据段,序列号为i;
1.服务器收到FIN(i)后,返回确认段ACK,序列号为i+1,关闭服务器读通道;
2.客户机收到ACK(i+1)后,关闭客户机写通道;
(此时,客户机仍能通过读通道读取服务器的数据,服务器仍能通过写通道写数据)
第二阶段 服务器发送完数据之后,向客户机发送一个FIN数据段,序列号为j;
3.客户机收到FIN(j)后,返回确认段ACK,序列号为j+1,关闭客户机读通道;
4.服务器收到ACK(j+1)后,关闭服务器写通道。
这是标准的TCP关闭两个阶段,服务器和客户机都可以发起关闭,完全对称。
可靠传输
TCP如何保证可靠传输呢?
TCP发送的报文段是交给IP层传送的,IP层只能提供尽最大努力服务,也就是不可靠服务,因此TCP必须采用恰当的措施才能使得两个传输层之间的通信变得可靠。
- TCP保证可靠传输的方式如下:
- 数据包校验: TCP会对整个报文段进行检验
- 滑动窗口机制: 以字节为单位进行
- 超时重传机制: 发送方在规定时间内没有收到确认就要重新发送报文
- 选择确认:当报文未按序到达时,若这些在接收窗口范围内,接收方就先收下然后将准确信息告诉发送方,让它不要重复发送。
- 流量控制和拥塞控制:通过发送窗口和接收窗口大小来实现。
停止等待协议 (发送窗口=1,接收窗口=1)
停止等待就是每发送完一个分组就停止发送,等待对方的确认,在收到确认后再发送下一个分组,停止等待协议分为以下几种情况:
无差错情况
A发送分组M1,发完就暂停发送,等待B的确认。B收到M1后就向A确认,A在收到对M1的确认后,就再发送下一个分组M2。同样在收到B对M2的确认后,再发送M3超时重传
可靠传输协议是只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组,这就叫超时重传,要实现超时重传,就要在每发送完一个分组设置一个超时计时器,如果在超时计时器到期之前收到之前对方的确认,就撤销已设置的超时计时器。
1 | 注意: |
- 确认丢失
B发送的对M1的确认丢失,A在设定的超时重传时间内没有收到确认,A在超时计时器到期后就要重传M1,现在应注意B的动作,假设B又收到了重传的分组M1,这时应该采取两个行动:
1.1 丢弃重复的M1,不向上层交付
1.2 重新向A发送确认,不能认为已经发送给确认就不再发送,因为A之所以重传M2就表示A没有收到对M2的确认。
- 确认超时
传输过程中没有出现差错,但B对分组M1的确认迟到了,A会收到重复的确认,对重复的确认的处理很简单:收下后丢弃。B仍然会收到重复的M1,并且同样要丢弃重复的M1,并重传确认分组。
滑动窗口协议
停止等待协议的接受方的窗口和发送方的窗口大小都是1,1个比特就够表示了,所以也叫1比特滑动窗口协议,发送方这时自然发送每次只能发送一个,并且必须等待这个数据包的ACK,才能发送下一个。
停止等待协议每发送一帧就要停止等待知道接收到确认帧,这样每次等待的时候不能传送数据使得传输效率很低,
那么我们就不能先连发几个包等他一起确认吗?
把两个包一起发送,然后一起确认,所花的时间只是一个来回的时间
每次发送多少个包过去是最优解呢?
把第一个和第二个包发送过去后,收到第一个确认包就把第三个包发送过去,而不是等到第二个包的确认包才去发第三个包
位于发送窗口内的5个分组都可以连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了,连续ARQ协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。
接收方采用累积确认的方式,这就是说,接收方不必对收到的分组逐个发送确认,而是可以在收到几个分组后,对按序到达的最后一个分组发送确认,这就表示到这个分组为止的所有分组都已正确收到了。
滑动窗口协议分为两种,一种是后退N帧协议,一种是选择重传协议。
后退N帧协议 (发送窗口>1,接收窗口=1)
如果发送方发送了前9个分组,而中间的第2个分组丢失,这是接收方只能对前两个分组发出确认,发送方无法知道后面7个分组的下落,而只好把后面的7个分组都再重传一次,这就叫做Go-back-N(回退N),表示需要再退回来重传已发送的N个分组
选择重传协议 (发送窗口>1,接收窗口>1)
返回N协议简化了接收方的处理过程,接收方只需要跟踪一个变量,并且不需要对失序到达的分组缓存,而是简单地把失序到达的分组他们丢掉。
选择重传协议是在后退N帧的基础上进行了改进,选择重传协议原理很简单,接收端总会缓存所有收到的帧,当某个帧出现错误时,只会要求重传这一个帧,只有当某个序号后的所有帧都正确收到后,才会一起提交给高层应用
选择重传协议的接收窗口和发送窗口一样大(2^m-1) 比返回N协议的窗口(2^m)小了一倍
选择重传的接收窗口与发送窗口一样大
.选择重传协议允许与接受窗口一样多的分组失序到达,并保存这些失序到达的分组,直到连续的一组分组被交付给应用层.因为发送窗口与接收窗口是相同的,所以发送出来的所有分组都可以失序到达,而且会被保留直到交付为止.但是必须强调一点,在一个可靠的协议中,接收方永远不会把分组失序地交给应用层.在他们被交付给应用层之前,先要等待那些更早发出来的分组到达.
理论上选择重传协议要为每个分组使用一个计时器.当某个计时器超时后,只有相应的分组被重传.换而言之,返回N协议将所有的分组当做一个整体对待,而选择重传协议则分别对待每一个分组.但是大多数SR的运输层仅使用了一个计时器. 注意只使用一个计时器而做到跟踪所有发出去的分组的情况的做法是:标记发出分组,当ACK=Sf 时,将窗口滑过所有连续的已确认的分组,如果还有未确认的分组,则则重发所有检测到的未被确认的分组并重启计时器,如果所有分组都被确认了则停止计时器.
面向字节流
不管发送方一次性提交给 TCP 的缓冲是多大的数据,对于TCP本身来说它会根据实际的情况来进行划分,比如发送方发送10字节,TCP 并不是一次性传递10字节数据,它可能是拆分成3个字节和7个字节分俩次发送给接收方
流量控制
基于滑动窗口协议实现.TCP的滑动窗口主要有两个作用,一是提供TCP的可靠性,二是提供TCP的流控特性。同时滑动窗口机制还体现了TCP面向字节流的设计思路。
滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。
对照上图,现在假如发送窗口是客户端,接收窗口是服务端。当我们现在要继续发送数据的时候,可能由于接收方的接收窗口没有那么大,而发送方的发送窗口非常大,就会以更快的速率往后发,需要由接收窗口通过向TCP的报文首部字段中去更改窗口值去调整发送方的发送窗口大小。
拥塞控制
TCP的四种拥塞控制算法
慢开始
拥塞避免
快恢复
快重传
横轴代表 APP 交互或者轮循次数,纵轴代表窗口值的大小
上图中,一开始我们可以先发送一个报文,如果没有发生网络拥塞,就继续发送 2 个报文,依然没有发生网络拥塞,就继续翻倍,直到 16 个报文,这就是慢开始算法
当到达 16 报文数的时候,会采用拥塞避免的策略来进行发送报文的一个数量的线性增长。
当报文数到 24 的时候,就会发生网络拥塞,采用拥塞避免的乘法减小到值发送 1 一个报文,来减少对网络层面带来的压力,然后再重新开始 慢开始算法增长,同时限定一个新的门限值.
UDP
UDP 是一个简单的传输层协议。和 TCP 相比,UDP 有下面几个显著特性:
UDP 缺乏可靠性。UDP 本身不提供确认,序列号,超时重传等机制。UDP 数据报可能在网络中被复制,被重新排序。即 UDP 不保证数据报会到达其最终目的地,也不保证各个数据报的先后顺序,也不保证每个数据报只到达一次
UDP 数据报是有长度的。每个 UDP 数据报都有长度,如果一个数据报正确地到达目的地,那么该数据报的长度将随数据一起传递给接收方。而 TCP 是一个字节流协议,没有任何(协议上的)记录边界。
UDP 是无连接的。UDP 客户和服务器之前不必存在长期的关系。UDP 发送数据报之前也不需要经过握手创建连接的过程。
UDP 支持多播和广播。