1 TCP简介
CP的全称是Transmission Control Protocol,即传输控制协议,TCP工作在传输层上
其职责是:实现主机间进程到进程的通信,其次还需要保证可靠性(不是安全性,换言之不能保证安全性)
2 特点
1)点对点:一个发送方,一个接收方
2)可靠:可靠的、按序的字节流
3)流水线机制:
TCP拥塞控制和流量控制机制 设置窗口尺寸 4)发送方/接收方缓存 5)全双工:同一连接中能够传输双向数据流 6)面向连接 通信双方在发送数据之前必须建立连接 连接状态只在连接的两端中维护,在沿途节点中并不维护状态 TCP连接包括:两台主机上的缓存、连接状态变量、socket等3 TCP的可靠
TCP会尽自己所能,尽量将数据发送给对方;但并不能保证100%可以发送给对方
TCP会在数据发送不到对方的情况下,会给应用层一个错误提示,告知用户发送失败
TCP可以保证接收方(应用层)严格按照发送时的数据顺序接收
TCP保证数据不会出现无意间的损坏(UDP 也做到这点)
TCP尽可能地维护网络质量
4 TCP报文格式
4.1 端口号
源端口号:报文的发送端口
目的端口号:报文的就收端口
4.2 序号(seq)
序号(Sequence Number):TCP传输过程中,在发送端出的字节流中,传输报文中的数据部分的每一个字节都有它的编号。
序号(Sequence Number)的语义与SYN控制标志(ControlBits)的值有关。根据控制标志(Control Bits)中的SYN是否为1,序号(SequenceNumber)表达不同的含义:
当SYN = 1时,当前为连接建立阶段,此时的序号为初始序号ISN((Initial Sequence Number),通过算法来随机生成序号;
当SYN = 0时在数据传输正式开始时,第一个报文的序号为 ISN +1,后面的报文的序号,为前一个报文的SN值+TCP报文的净荷字节数(不包含TCP头)。
比如,如果发送端发送的一个TCP帧的净荷为12byte,序号为5,则发送端接着发送的下一个数据包的时候,序号的值应该设置为5+12=17。
4.3 确认序号ACK(AcknowledgmentNumber)
标识了报文接收端期望接收的字节序列。如果设置了ACK控制位,确认序号的值表示一个准备接收的包的序列码,注意,它所指向的是准备接收的包,也就是下一个期望接收的包的序列码。
4.4 控制标志位
控制标志(Control Bits)共6个bit位,具体的标志位为:URG、ACK、PSH、RST、SYN、FIN。6个标志位的说明,如下面所示。
URG 占1位,表示紧急指针字段有效。URG位指示报文段里的上层实体(数据)标记为“紧急”数据。当URG=1时,其后的紧急指针指示紧急数据在当前数据段中的位置(相对于当前序列号的字节偏移量),TCP接收方必须通知上层实体。
ACK 占1位,置位ACK=1表示确认号字段有效;TCP协议规定,接建立后所有发送的报文的ACK必须为1;当ACK=0时,表示该数据段不包含确认信息。当ACK=1时,表示该报文段包括一个对已被成功接收报文段的确认序号Acknowledgment Number,该序号同时也是下一个报文的预期序号。
PSH 占1位,表示当前报文需要请求推(push)操作;当PSH=1时,接收方在收到数据后立即将数据交给上层,而不是直到整个缓冲区满。
RST 占1位,置位RST=1表示复位TCP连接;用于重置一个已经混乱的连接,也可用于拒绝一个无效的数据段或者拒绝一个连接请求。如果数据段被设置了RST位,说明报文发送方有问题发生。
SYN 占1位,在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1。 综合一下,SYN置1就表示这是一个连接请求或连接接受报文。
FIN 占1位,用于在释放TCP连接时,标识发送方比特流结束,用来释放一个连接。当 FIN = 1时,表明此报文的发送方的数据已经发送完毕,并要求释放连接。
在连接建立的三次握手过程中,若只是单个SYN置位,表示的只是建立连接请求。如果SYN和ACK同时置位为1,表示的建立连接之后的响应.
4.5 窗口大小
长度为16位,共2个字节。此字段用来进行流量控制。流量控制的单位为字节数,这个值是本端期望一次接收的字节数。
4.6 检验和
长度为16位,共2个字节。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,接收端用于对收到的数据包进行验证
5 TCP的三次握手和四次挥手
5.1 三次握手过程
TCP连接的建立时,双方需要经过三次握手,具体过程如下:
(1)第一次握手
Client进入SYN_SENT状态
发送一个[SYN帧]来主动打开传输通道,该帧的SYN标志位被设置为1
同时会带上Client分配好的SN序列号,该SN是根据时间产生的一个随机值,通常情况下每间隔4ms会加1
除此之外,[SYN帧]还会带一个MSS(最大报文段长度)可选项的值,表示客户端发送出去的最大数据块的长度。
(2)第二次握手
Server端在收到[SYN帧]之后,会进入SYN_RCVD状态
同时返回[SYN&ACK帧]给Client,主要目的在于通知Client,Server端已经收到SYN消息,现在需要进行确认。
Server端发出的[SYN&ACK帧]的ACK标志位被设置为1
其确认序号Ack(Acknowledgment Number)值被设置为Client的SN+1;
Server端也随机生成一个SN序号;
也可以携带一个MSS,[SYN&ACK帧]的MSS(最大报文段长度)表示的是Server端的最大数据块长度。
(3)Client在收到Server的第二次握手[SYN&ACK确认帧}之后
首先将自己的状态会从SYN_SENT变成ESTABLISHED,表示自己方向的连接通道已经建立成功,Client可以发送数据给Server端了。
然后,Client发ACK帧给Server端,该ACK帧的ACK 标志位被设置为1
其确认序号AN(Acknowledgment Number)值被设置为Server端的SN序列号+1。
还有一种情况,Client可能会将ACK帧和第一帧要发送的数据,合并到一起发送给Server端。。
(4)Server端在收到Client的ACK帧之后
从SYN_RCVD状态会进入ESTABLISHED状态
至此,Server方向的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成。
5.2 四次挥手具体过程
(1)第一次挥手
主动断开方(可以是客户端,也可以是服务器端),向对方发送一个FIN结束请求报文,此报文的FIN位被设置为1
正确设置Sequence Number(序列号)和Acknowledgment Number(确认号)。
发送完成后,主动断开方进入FIN_WAIT_1状态,这表示主动断开方没有业务数据要发送给对方,准备关闭SOCKET连接了。
(2)第二次挥手
正常情况下,在收到了主动断开方发送的FIN断开请求报文后,被动断开方会发送一个ACK响应报文
报文的Acknowledgment Number(确认号)值为断开请求报文的Sequence Number(序列号)加1,该ACK确认报文的含义是:“我同意你的连接断开请求”。
之后,被动断开方就进入了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知高层的应用进程,对方向本地方向的连接已经关闭,对方已经没有数据要发送了,若本地还要发送数据给对方,对方依然会接受。被动断开方的CLOSE-WAIT(关闭等待)还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
主动断开方在收到了ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。
(3)第三次挥手
在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截止后,被动断开方会向主动断开方发送一个FIN+ACK结束响应报文,表示被动断开方的数据都发送完了,然后,被动断开方进入LAST_ACK状态。
(4)第四次挥手
主动断开方收在到FIN+ACK断开响应报文后,还需要进行最后的确认,向被动断开方发送一个ACK确认报文
然后,自己就进入TIME_WAIT状态,等待超时后最终关闭连接。
处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,如果期间没有收到其他报文,则证明对方已正常关闭,主动断开方的连接最终关闭。
被动断开方在收到主动断开方的最后的ACK报文以后,最终关闭了连接,自己啥也不管了。
6 握手挥手细节
6.1 处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,才真正关闭连接通道,其等待的时间为什么是2MSL
2MSL翻译过来就是两倍的MSL。MSL全称为Maximum Segment Lifetime,指的是一个TCP报文片段在网络中最大的存活时间,具体来说,2MSL对应于一次消息的来回(一个发送和一个回复)所需的最大时间。如果直到2MSL,主动断开方都没有再一次收到对方的报文(如FIN报文),则可以推断ACK已经被对方成功接收,此时,主动断开方将最终结束自己的TCP连接。所以,TCP的TIME_WAIT状态也称为2MSL等待状态。
有关MSL的具体的时间长度,在RFC1122协议中推荐为2分钟。在SICS(瑞典计算机科学院)开发的一个小型开源的TCP/IP协议栈——LwIP开源协议栈中MSL默认为1分钟。在源自Berkeley的TCP协议栈实现中MSL默认长度为30秒。总体来说,TIME_WAIT(2MSL)等待状态的时间长度,一般维持在1-4分钟之间。
通过三次握手建立连接和四次挥手拆除连接,一次TCP的连接建立及拆除,至少进行7次通信,可见其成本是很高的。
6.2 为什么关闭连接的需要四次挥手,而建立连接却只要三次握手
关闭连接时,被动断开方在收到对方的FIN结束请求报文时,很可能业务数据没有发送完成,并不能立即关闭连接,被动方只能先回复一个ACK响应报文,告诉主动断开方:“你发的FIN报文我收到了,只有等到我所有的业务报文都发送完了,我才能真正的结束,在结束之前,我会发你FIN+ACK报文的,你先等着”。所以,被动断开方的确认报文,需要拆开成为两步,故总体就需要四步挥手。
而在建立连接场景中,Server端的应答可以稍微简单一些。当Server端收到Client端的SYN连接请求报文后,其中ACK报文表示对请求报文的应答,SYN报文用来表示服务端的连接也已经同步开启了,而ACK报文和SYN报文之间,不会有其他报文需要发送,故而可以合二为一,可以直接发送一个SYN+ACK报文。所以,在建立连接时,只需要三次握手即可。
6.3 为什么连接建立的时候是三次握手,可以改成两次握手
三次握手完成两个重要的功能:一是双方都做好发送数据的准备工作,而且双方都知道对方已准备好;二是双方完成初始SN序列号的协商,双方的SN序列号在握手过程中被发送和确认。
如果把三次握手改成两次握手,可能发生死锁。两次握手的话,缺失了Client的二次确认ACK帧,假想的TCP建立的连接时二次挥手,可以如下图所示:
在假想的TCP建立的连接时二次握手过程中,Client发送Server发送一个SYN请求帧,Server收到后发送了确认应答SYN+ACK帧。按照两次握手的协定,Server认为连接已经成功地建立了,可以开始发送数据帧。这个过程中,如果确认应答SYN+ACK帧在传输中被丢失,Client没有收到,Client将不知道Server是否已准备好,也不知道Server的SN序列号,Client认为连接还未建立成功,将忽略Server发来的任何数据分组,会一直等待Server的SYN+ACK确认应答帧。而Server在发出的数据帧后,一直没有收到对应的ACK确认后就会产生超时,重复发送同样的数据帧。这样就形成了死锁。
6.4 为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间
原因之一:主动断开方等待2MSL的时间,是为了确保两端都能最终关闭。假设网络是不可靠的,被动断开方发送FIN+ACK报文后,其主动方的ACK响应报文有可能丢失,这时候的被动断开方处于LAST-ACK状态的,由于收不到ACK确认被动方一直不能正常的进入CLOSED状态。在这种场景下,被动断开方会超时重传FIN+ACK断开响应报文,如果主动断开方在2MSL时间内,收到这个重传的FIN+ACK报文,会重传一次ACK报文,后再一次重新启动2MSL计时等待,这样,就能确保被动断开方能收到ACK报文,从而能确保被动方顺利进入到CLOSED状态。只有这样,双方都能够确保关闭。反过来说,如果主动断开方在发送完ACK响应报文后,不是进入TIME_WAIT状态去等待2MSL时间,而是立即释放连接,则将无法收到被动方重传的FIN+ACK报文,所以不会再发送一次ACK确认报文,此时处于LAST-ACK状态的被动断开方,无法正常进入到CLOSED状态。
原因之二:防止“旧连接的已失效的数据报文”出现在新连接中。主动断开方在发送完最后一个ACK报文后,再经过2MSL,才能最终关闭和释放端口,这就意味着,相同端口的新TCP新连接,需要在2MSL的时间之后,才能够正常的建立。2MSL这段时间内,旧连接所产生的所有数据报文,都已经从网络中消失了,从而,确保了下一个新的连接中不会出现这种旧连接请求报文。
6.5 如果已经建立了连接,但是Client端突然出现故障了怎么办
TCP还设有一个保活计时器,Client端如果出现故障,Server端不能一直等下去,这样会浪费系统资源。每收到一次Client客户端的数据帧后,Server端都的保活计时器会复位。计时器的超时时间通常是设置为2小时,若2小时还没有收到Client端的任何数据帧,Server端就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,Server端就认为Client端出了故障,接着就关闭连接。如果觉得保活计时器的两个多小时的间隔太长,可以自行调整TCP连接的保活参数
7 TCP可靠数据传输
7.1 策略
1)流水线机制
2)累计确认机制
3)单一重传机制
4)触发重传的事件
超时
收到重复的应答
7.2 重传示例
A:发送一个数据报,序号为92,数据大小为8b
B:应答ACK100(92+8=100),但是丢失了
A:发送一个数据报,序号为100,数据大小为20
B:应答ACK120
A:收到ACK120,更新sendbase为120,表示120及以前的全部正确传输完成
A:发送一个数据报,序号为92,数据大小为8b
A:发送一个数据报,序号为100,数据大小为20
B:应答ACK100
B:应答ACK120
A:由于网络等原因,ACK100的应答超时了还没有到达,重发序号为92的数据
A:收到ACK100,更新sendbase为100,表示100及以前的全部正确传输完成
A:收到ACK120,更新sendbase为120,表示120及以前的全部正确传输完成
B:再次收到序号为92的数据,重复了,舍弃,应答ACK120
A:收到ACK120,更新sendbase为120,表示120及以前的全部正确传输完成
7.3 超时时时间间隔重置
TCP的实现中,如果发生超时,超时时间间隔将重新设置,将超时时间加倍7.4 快速重传机制
如果sender收到对同一数据的3个ACK,则假定该数据之后的段已经丢失,发起重传8 TCP的流量控制
利用滑动窗口机制可以很方便地在TCP连接上实现发送方流量控制。通过接收方的确认报文中的窗口字段(标识接收方的能够处理的大小),发送方能够准确地控制发送字节数。9 拥塞控制
9.1 简介
前面的流量控制是避免发送方的数据填满接收方的缓存,但并不知道网络中发生了什么。
一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。在网络出现拥堵时,如果继续发送大量的数据包,可能会导致数据包时延、丢失,这时 TCP 就会重传数据,但是⼀重传就会导致⽹络的负担更重,于是会导致更⼤的延迟以及更多的丢包,这个情况就会进⼊恶性循环被不断地放⼤…
于是,就有了拥塞控制,控制的⽬的就是避免「发送⽅」的数据填满整个⽹络。为了在「发送⽅」调节所要发送数据的数据量,定义了⼀个叫做「拥塞窗⼝」的概念。拥塞窗⼝ cwnd是发送⽅维护的⼀个的状态变量,它会根据⽹络的拥塞程度动态变化的。
前⾯提到过发送窗⼝ swnd 和接收窗⼝ rwnd 是约等于的关系,那么由于加⼊了拥塞窗⼝的概念后,此时发送窗⼝的值是swnd = min(cwnd, rwnd),也就是拥塞窗⼝和接收窗⼝中的最⼩值。
9.2 拥塞控制四个算法
慢启动(slow-start)、拥塞避免(congestion avoidance)、快重传(fast retransmit)、快恢复(fast recovery)。
9.2.1 慢启动
TCP 在刚建⽴连接完成后,⾸先是有个慢启动的过程,这个慢启动的意思就是⼀点⼀点的提⾼发送数据包的数量。慢启动的算法规则:当发送⽅每收到⼀个 ACK,拥塞窗⼝ cwnd 的⼤⼩就会加 1。
连接建⽴完成后,⼀开始初始化 cwnd = 1 ,表示可以传⼀个 MSS ⼤⼩的数据。
当收到⼀个 ACK 确认应答后,cwnd 增加 1,于是⼀次能够发送 2 个
于是又发送2个,当收到 2 个的 ACK 确认应答后, cwnd 增加 2,于是就可以⽐之前多发2 个,所以这⼀次能够发送 4 个
于是又发送4个,当这 4 个的 ACK 确认到来的时候,每个确认 cwnd 增加 1, 4 个确认 cwnd 增加 4,于是就可以⽐之前多发 4个,所以这⼀次能够发送 8 个。
可以看出慢启动算法,发包的个数是指数性的增⻓。有⼀个叫慢启动⻔限 ssthresh (slow start threshold)状态变量。
当 cwnd < ssthresh 时,使⽤慢启动算法。
当 cwnd >= ssthresh 时,就会使⽤「拥塞避免算法」。
9.2.2 拥塞避免
前⾯说道,当拥塞窗⼝ cwnd 「超过」慢启动⻔限 ssthresh 就会进⼊拥塞避免算法。⼀般来说 ssthresh 的⼤⼩是 65535 字节。那么进⼊拥塞避免算法后,它的规则是:每当收到⼀个 ACK 时,cwnd 增加 1/cwnd。
接上前⾯的慢启动的例子,现假定最初ssthresh 为 8
当 8 个 ACK 应答确认到来时,每个确认增加 1/8,8 个 ACK 确认 cwnd ⼀共增加 1,于是这⼀次能够发送 9个 MSS ⼤⼩的数据,变成了线性增⻓。
拥塞避免算法就是将原本慢启动算法的指数增⻓变成了线性增⻓,还是增⻓阶段,但是增⻓速度缓慢了⼀些。
9.2.3 重传
当⽹络出现拥塞,也就是会发⽣数据包重传,重传机制主要有两种:超时重传、快速重传。
当发生超时重传时:
ssthresh 设为此时的cwnd /2
cwnd 直接设为1
重新进行慢启动
当发生快速重传时:
cwnd = cwnd/2 ,也就是设置为原来的⼀半;
ssthresh = cwnd ;也就是设置为原来cwnd 的⼀半(和超时重传一样)