校验和
TCP检验和的计算与UDP一样,在计算时要加上12byte的伪首部,检验范围包括TCP首部及数据部分。
计算方法为:在发送方将整个报文段分为多个16位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算。
如果接收方校验和与发送方不一致,那么数据一定传输有误。但是如果接收方比对校验和与发送方一致,数据不一定传输成功。
序列号与确认号
序列号Seq:TCP将每个字节的数据都进行了编号,一般序列号Seq为将要传输的数据的第一个编号
确认号Ack:TCP收到数据后,期望下一次收到数据的第一个编号
序列号的作用:
- 保证可靠性,通过Ack可以判断接收方是否少接收数据
- 保证数据的按序到达,因为每个字节的数据都有编号
- 提高效率,可实现多次发送,一次确认
- 去除重复数据,由于网络阻塞加上超时重传机制,可能发送了两次相同的数据,接收方丢弃第二次发来的数据
确认号的作用:
- 可靠的数据传输,Ack的值表示这个值之前的数据都已经按序到达了
- 延迟确认,如果连续收到两个TCP包,并不一定需要Ack两次,只要回复最终的Ack就可以了,可以降低网络流量。
超时重传
因为有确认应答与序列号机制,在发送方发送一部分数据后,会等待接收方发送的ACK报文,并解析ACK报文,判断数据是否传输成功。如果发送方发送完数据后,迟迟没有等到接收方的ACK报文,这该怎么办呢?而没有收到ACK报文的原因可能是什么呢?
首先,发送方没有介绍到响应的ACK报文原因可能有两点:
- 数据在传输过程中由于网络故障导致丢包,接收方没有接收到数据
- 接收方接收到了数据,但是ACK报文响应却由于网络故障导致丢包
超时重传:发送方在发送完数据后等待一个时间,时间到达没有接收到ACK报文,那么对刚才发送的数据进行重新发送。如果是第一个原因,接收方收到二次重发的数据后,便进行ACK应答。如果是第二个原因,接收方发现接收的数据已存在(判断存在的根据就是序列号,所以上面说序列号还有去除重复数据的作用),那么直接丢弃,仍旧发送ACK应答。
那么发送方发送完毕后等待的时间是多少呢?
由于TCP传输时保证能够在任何环境下都有一个高性能的通信,因此这个最大超时时间(也就是等待的时间)是动态计算的。
在Linux中超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。重发一次后,仍未响应,那么等待2500ms的时间后,再次重传。等待4500ms的时间继续重传。以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接。
滑动窗口 (TCP流量控制)
接收端在接收到数据后,对其进行处理。如果发送端的发送速度太快,导致接收端的结束缓冲区很快的填充满了。此时如果发送端仍旧发送数据,那么接下来发送的数据都会丢包,继而导致丢包的一系列连锁反应,而TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制。
在TCP协议的报头信息当中,有一个16位字段的窗口大小。在介绍这个窗口大小时我们知道,窗口大小的内容实际上是接收端接收数据缓冲区的剩余大小。这个数字越大,证明接收端接收缓冲区的剩余空间越大,网络的吞吐量越大。接收端会在确认应答发送ACK报文时,将自己的即时窗口大小填入,并跟随ACK报文一起发送过去。而发送方根据ACK报文里的窗口大小的值的改变进而改变自己的发送速度。如果接收到窗口大小的值为0,那么发送方将停止发送数据。并定期的向接收端发送窗口探测数据段,让接收端把窗口大小告诉发送端。
窗口大小不受16位窗口大小(64k)限制,在TCP首部40字节选项中还包含一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位
拥塞控制
直接看别人的,不是重点
三次握手(重点)
TCP协议为什么需要三次握手?
主要原因是为了防止旧的重复连接引起连接混乱问题
比如在网络环境比较复杂的情况,客户端可能会连续发送多次请求。如果只设计成两次握手的情况,服务端只能一直接收请求,然后返回请求信息,也不知道客户端是否请求成功。这些过期请求的话就会造成网络连接的混乱。
所以设计成三次握手的情况,客户端在接收到服务端SEQ+1的返回消息之后,就会知道这个连接是历史连接,所以会发送报文给服务端,告诉服务端。
所以TCP设计成三次握手的目的就是为了避免重复连接。为了节省资源,三次握手就可以符合实际情况,所以就没必要设计成四次握手、五次握手等情况。
建立过程为:
- server首先建立传输控制块TCB,进入LISTEN(收听)状态,等待用户的连接请求。如有,则建立连接。(c语言中为server端调用socket函数、bind函数和listen函数的过程)
- client建立传输控制块TCB,然后向server发送连接请求报文段,报文段中首部的同步位SYN=1,同时选择一个序列号seq=x(随机),TCP规定SYN报文段不携带数据,但要消耗一个序列号。然后A进入SYN-SENT(同步已发送)状态。(c语言中为client端调用socket函数和connect函数的过程)
- server收到请求后,如同意建立连接,就向client发送确认报文段。此时SYN=1、ACK=1,确认号ack=x+1,同时选择一个序列号seq=y(随机),这个报文也不携带数据,但要消耗一个序列号。然后B进入SYN-RCVD状态(同步收到)。
- client收到server的确认后,还要向server发送确认。确认报文段的ACK=1,确认号ack=y+1,seq=x+1。TCP规定,ACK报文段可以携带数据,而如果不携带数据则不消耗序列号,此时下一个报文段的序列号仍为seq=x+1。这时,连接就建立成功了,A进入ESTABLISHED状态(已建立连接状态)。
- 当server收到client的确认后,也进入ESTABLISHED状态,此时就可以进行数据传输了。
四次挥手(重点)
1.为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当server端收到client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。
但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文(否则可能触发超时重传),告诉client端,“你发的FIN报文我收到了”。只有等到我server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
2.为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
在连接释放时,连接的两方都要同意才能够释放成功
当client的数据传送完后,就可以向其TCP发起连接释放了,此后停止再发送数据,主动关闭TCP连接。首先client向server发送一个FIN报文段,报文段首部FIN=1,序列号seq=u(u为最后传送的数据的序列号加1),然后client进入FIN-WAIT-1(终止等待1)状态。FIN报文段不能携带数据,但要消耗一个序列号。
server收到释放连接的报文段后即发出确认报文段,报文首部ACK=1,ack=u+1,seq=v(v等于server前面传送过的数据的序列号加1),然后B进入CLOSE-WAIT(关闭等待)状态。这时从client到server这个方向的连接就释放了,TCP连接就处于半关闭状态。(注意:此后client不能主动向server发送数据,但是可以发送确认报文段,也就是说client仍要接收来自server的报文,因为从server到client这个方向的连接还没有关闭)
当client收到server的确认报文后,就进入FIN-WAIT-2(终止等待2)状态,等待server发出的连接释放报文段。
当server的数据发送完毕后,其应用进程就通知TCP释放连接。server向client发送FIN报文,报文段首部FIN=1,ack=u+1(重复发送上一次已经发送过的确认号),seq=w(w为server最后发送报文段的序列号加1)。然后B进入LAST-ACK(最后确认)状态,等待client的确认。
client在接收到server的连接释放报文后,必须进行确认。client向server发送的确认报文段中报文首部ACK=1,ack=w+1,seq=u+1。然后client进入TIME-WAIT(时间等待)状态(如果无差错,此状态时间为2MSL),注意,此时TCP连接还没有释放掉,必须经过TIME-WAIT设置的时间2MSL后,client撤销相应的传输控制块TCB,才进入CLOSED状态,结束了此次TCP连接。MSL叫做最长报文段寿命,RFC793建议设为2分钟,但在现在实际网络情况中,常用值有三种:30秒,1分钟,2分钟。必须要在client进入CLOSED状态后才能开始建立下一个新的连接。
server收到client的确认报文后,也进入CLOSED状态,撤销相应的传输控制块TCB,此时,TCP连接全部断开。
这样TCP四次挥手完成。