TCP协议详解
TCP协议是TCP/IP协议族里的另一个极为重要的协议,按照TCP/IP五层模型来划分,IP协议层位于传输层,基于它实现的广泛应用的应用层协议有http/ftp等;位于该层的还有UDP协议,关于它们的区别与联系,我们会在后文进行详细的讲解;
TCP协议特点
面向字节流,面向连接,可靠传输,一对一连接,无法像UDP一样广播多播;
面向连接的具体表现为,使用TCP协议进行通信,双方必须要先建立连接,并为此向内核申请到资源,才可以开始数据的读写,这也直接奠定了TCP可靠传输的基础;当然,TCP的可靠传输还依赖了许多机制,这包括了连接管理,确认应答机制,超时重传机制,确认序号/包序管理机制以及校验和机制,在这之中值得一提的是连接管理中的保活机制:当长时间无通信数据时(大约7200秒(用systemctrl 查看grepalive)),便开始每隔一会(75)秒发送一个保活探测包,直到对端对探测包做出回应,但如果连发9次都没有得到回复,便认为连接断开,(这在程序中的直接体现就是我们的recv函数返回0了,send触发SIGPIPE异常);
-
这些机制帮助TCP协议实现了可靠传输,但这是以牺牲了部分性能为前提的,而有的性能的牺牲是没有必要的,因此有了滑动窗口机制,流量控制机制,快速重传机制,拥塞窗口机制,延时应答机制,捎带应答机制来降低不必要的性能损失
- 滑动窗口机制:由于对每一个要发送的数据段,都需要接收到ACK确认应答后才能发送下一数据段,这样随着数据往返时间变长,通信效率会越来越差,于是规定可以一次发送多条数据,这么多无需等待确认应答就直接发送的数据就是窗口的大小,当接收到某条数据段的ACK确认回复时,开始发送下一数据段,这就形成了窗口的滑动,系统需要开辟发送缓冲区来维护窗口,当有的数据确认了应答,便将其从缓冲区删除;窗口大小决定了网络的吞吐量;
- 流量控制机制:如果发送端发送了大量的数据,但接收端没有这么多的空间接收,便会导致大量数据的丢失,所以流量控制机制规定,通信双方通过两方协议字段中的窗口大小来协商接下来应该发送的最大数据长度,窗口的大小不可大于当前接收区中空闲空间的大小
- 快送重传机制:当接收端接收数据的时候,如果前面序号数据没收到,却收到了后面的数据,它便认为这条数据有可能丢失,开始连续三次发送重传的ACK请求;如果发送端连续三次都收到这样的回复,那么便会重新发送该数据段。
- 拥塞窗口机制:前面我们提到了流量控制机制下通信双方所商议的窗口大小,这个窗口如果很大,一次发送很多数据,一旦网络环境不好,就会造成大量丢包,所以发送端维护一个拥塞窗口(CWND),限制发送端发送的数据最大大小,这个CWND随着每次对端的ACK回复快速增长,一旦丢包重传或传输超时时,判断拥塞发生,立刻初始化(IW:2~4SMSS),发送窗口大小变会根据min(RWND(接收通告窗口),CWND)来取值(闭环反馈控制),简言之就是慢启动,快增长,是一种探测方式
- 延时应答机制:接收端预留充足的时间让进程把数据从接收区拿走,最大限度保证窗口大小;
- 捎带应答机制:尽可能地避免纯报头确认回复;
至于面向字节流,当发送端数据进行多次读写操作时,TCP模块先把数据都放入发送缓冲区,然后发送时被封装成一个或多个报文段以字节为单位进行流式传输,这样的传输较为灵活,但不论在发送端的发送缓冲区还是接收端的接收缓冲区里,都会因为堆积的数据间没有明显的边界区分,进而产生的TCP粘包问题,为了应对这种情况,我们需要在应用层给数据增加边界,如特殊字符间隔(需要转义),数据定长(空间利用不灵活),在不定长数据的应用协议头部定义数据长度(UDP的做法);
TCP头部字段
16位端口号:告知主机该报文来自哪里(源端口),以及上传给哪个上层协议或应用程序(目的端口)客户端通常使用系统默认提供的端口号;
32位序号:在一次TCP通信过程中,统一传输方向上的字节流的每一个字节的编号,第一个字节是随机的,之后的字节序号是在此之上的偏移;
32位确认号:是对对端发送来的TCP报文段的相应,值为收到的YCP报文段序号值加1;
4位头部长度:表示该报文头部长度;最大60(15*4)字节;
6位标志位:URG紧急指针,ACK确认号,PSH立即读取,RST重建连接,SYN建立连接请求,FIN关闭链接请求;
16位窗口大小:告诉对端TCP接收缓冲区还有多少空闲,用以流量控制;
16位校验和:CRC校验数据和头部是否正确;
16紧急指针:一个正的偏移量,发送紧急数据时,其值与序号字段相加表示该紧急数据的下一字节的数据报的序号;
最大40字节的选项字段
TCP连接的建立与关闭(三次握手与四次挥手)
TCP连接的建立(三次握手):举个例子,当客户端想要与服务端建立TCP连接时,客户端会向服务端先发送一个包含SYN标志的同步报文段,用以发起连接请求。当服务端接收到该请求,便会回复一个包含ACK(确认收到回复)和SYN(也向客户端请求连接的)同步报文段;客户端收到后再回复一个ACK,确认收到了来自了服务端的同步报文段;此时客户端与服务端便都认为与对方建立了连接,此时TCP的连接建立了起来;
TCP连接的释放(四次挥手):当TCP通信的双方中客户端需要断开连接时,客户端会先向服务端发送包含FIN标志的结束报文段;服务端接收后,返回一个ACK确认回复,表示自己已经收到了断开请求,在处理完剩下的数据后,再发送FIN包,当客户端收到来自服务端的ACK与FIN包后,得知双方都已同意断开,并且也都处理完了剩余的数据,此时返回ACK确认回复,并且进入TIME_WAIT状态,等待两个MSL(报文最大生存周期)后退出连接,这是为了–1.可靠的终止TCP连接:防止最后一次ACK丢失,服务端端没有收到ACK回复还以为自己发送的FIN包没被对方收到,继续重新发送,而客户端会以复位报文段来回应,这不是服务器所期望的。2.保证让迟来的TCP报文有足够时间被丢弃:防止对后续的连接造成影响,比如此时有新的客户端在原有端口上线,还没建立连接就收到了属于原来的,携带数据的TCP报文;;但在实际编程中,我们有时会因为主动断开后的TIMEWAIT导致端口占用无法立刻重启,此时可以使用setsocketopt函数来设置socket允许重用本地地址和端口;
Q:为什么握手是三次,挥手是四次?
- 因为双方都需要确认对方是否具有收发数据的能力,防止网络延迟造成的误解;甲发送请求给乙,乙进行回复和连接请求,甲接收到回复后确定了乙具有收发的能力,并且回复乙的请求,乙收到后也确定了甲具有首发的能力,此时双方都确定后握手动作便完成了,四次没有必要,而两次握手的话,甲在发送了SYN请求后就退出了,乙收到后回复了ACK和自己的SYN,但没有人回复,无法保证甲的数据收发能力,因此是不安全的;
而挥手也是需要双方都同意断开连接,并且对方知晓的情况下才能断开,甲在发送了FIN包请求断开后,乙方可以立刻回复ACK确认,但乙此时仍要处理通信产生的数据,处理完后,才能准备好发送自己的FIN请求也断开连接,待同意后发送ACK回复后,才可以断开连接,这个过程较之握手的三次,多了数据处理的过程,因此需要四次。
Q:什么是半关闭状态?
TCP连接是全双工的,它允许两个方向的数据传输独立关闭,也就是说一端可以发送结束报文段(FIN报)给对方,告诉它本端已经完成数据发送,但依旧可以继续接受来自对方的数据(FIN/ACK等),直到对方也发送结束报文段以关闭连接,这种状态就叫做半关闭态
当访问不存在端口,异常中止连接,处理半打开连接时,TCP提供了发送复位报文段的方法来重置连接,但发送端所有排队等待发送的数据都将被丢弃;
TCP的状态转移
以客户端向服务端发起连接,客户端向服务端断开连接为例:
服务端:
- 【CLOSED->LISTEN】:服务端程序通过listen系统调用,进入listen状态,等待客户端连接;
- 【LISTEN->SYN_RCVD】:一旦服务端监听到连接请求,就将该请求放入内核等待队列,并且向客户端发送带有SYN标志的确认报文段,此时进入SYN_RECVD状态;
- 【STN_RECVD->ESTABLISHED】:服务端一旦受到来自客户端的确认报文,就进入ESTABLISHED阶段,此时连接建立起来,可以数据收发了
- 【ESTABLISHED->CLOSE_WAIT】: 当客户端主动关闭连接,服务端收到FIN包并返回确认报文段,进入CLOSE_WAIT状态;
- 【CLOSE_WAIT->LAST_ACK】:当服务端处理完剩下的数据,真正调用close时,向客户端发送FIN包,进入LAST_ACK状态,等待最后一个last_ack到来;
- 【LAST_WAIT->CLOSED】:收到客户端对FIN包的确认回复,彻底断开连接;
客户端: - 【CLOSED->SYN_SEND】:客户端调用conect发起连接请求,向服务端发送SYN同步报文段;进入SYN_SEND状态;
- 【SYN_SEND->ESTABLISHED】:收到来自服务器的确认报文段,进入ESTABLISHED状态,开始数据收发;
- 【ESTABLISHED->FIN_WAIT_1】: 客户端主动调用close,向服务端低啊用结束报文段,进入FIN_WAIT1,等待服务器的确认回复;
- 【FIN_WAIT_1>FIN_WAIT_2】:收到服务器的回复报文段,进入FIN_WAIT2,等待服务端处理完成工作后发送的结束报文段;
- 【FIN_WAIT_2->TIME_WAIT】:收到来自服务端的结束报文段,发送ACK确认回复;
- 【TIME_WAIT->CLOSED】:等待2MSL时间后进入CLOSED;
TCP的交互数据流与成块数据流
交互数据仅包含很少的字节,使用交互数据的应用协议对实时性要求很高(telnet,ssh);而成块数据的长度则通常为TCP报文段所允许的最大数据长度,使用它的应用协议对于传输效率要求很高(ftp);