一、TCP的首部
TCP数据被封装在一个IP数据报中,如图17-1所示:
TCP首部的数据格式,如果不计任选字段,它通常是20个字节,最大长度为60:
源端口号:表示发送端端口号,字段长 16 位;
目标端口号:表示接收端端口号,字段长 16 位;
序列号:字段长 32 位,占4个字节,序号的范围为[0,4284967296]。由于TCP是面向字节流的,在一个TCP连接中传送的字节流中的每一个字节都按顺序编号,首部中的序号字段则是指本报文段所发送的数据的第一个字节的序号。另外,序号是循环使用的,当序号增加到最大值时,下一个序号就又回到了0。
确认应答:字段长 32 位,当ACK标志位为1时有效,表示期望收到的下一个报文段的第一个数据字节的序号。确认号为N,则表明到序号N-1为止的所有数据字节都已经被正确地接收到了
首部长度(数据偏移):该字段表示 TCP 所传输的数据部分应该从 TCP 包的哪个位置开始计算,可以把它看作是 TCP 首部的长度。该字段长 4 位,单位是 4 字节(即 32 位)。不包括选项字段的话,TCP 首部长度为 20 个字节,因此,数据偏移字段可设置为 5。反之,若该字段值为 5,那么说明从 TCP 包的一开始到 (5*单位4字节)20 字节为止都是 TCP 首部,如果数据偏移字段设置为最大15,TCP 包的一开始到 (15*单位4字节)60 字节为止都是 TCP 首部则余下的部分为 TCP 数据;
RESV保留:该字段主要是为了以后扩展时使用,其长度为 4 位,一般设置为 0,但即使收到的包在该字段不为 0,此包也不会被丢弃;
控制位:字段长为 8 位,每一位从左到右分别为 CWR、ECE、URG、ACK、PSH、RST、SYN、FIN。这些控制标志也叫做控制位。当他们的对应位上值为 1 时,具体含义如下:
CWR:CWR 标志与后面的 ECE 标志都用于 IP 首部的 ECN 字段,ECE 标志为 1 时,则通知对方已将拥塞窗口缩小;
ECE:若其值为 1 则会通知对方,从对方到这边的网络有阻塞。在收到数据包的 IP 首部中 ECN 为 1 时将 TCP 首部中的 ECE 设为 1.;
URG:该位设为 1,表示包中有需要紧急处理的数据,对于需要紧急处理的数据,与后面的紧急指针有关;
ACK:该位设为 1,确认应答的字段有效,TCP规定除了最初建立连接时的 SYN 包之外该位必须设为 1;
PSH:该位设为 1,表示需要将收到的数据立刻传给上层应用协议,若设为 0,则先将数据进行缓存;
RST:该位设为 1,表示 TCP 连接出现异常必须强制断开连接;
SYN:用于建立连接,该位设为 1,表示希望建立连接,并在其序列号的字段进行序列号初值设定;
FIN:该位设为 1,表示今后不再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位置为 1 的 TCP 段。每个主机又对对方的 FIN 包进行确认应答之后可以断开连接。不过,主机收到 FIN 设置为 1 的 TCP 段之后不必马上回复一个 FIN 包,而是可以等到缓冲区中的所有数据都因为已成功发送而被自动删除之后再发 FIN 包;
窗口大小:该字段长 16 位,用于通知从相同 TCP 首部的确认应答号所指位置开始能够接收的数据大小(8 位字节)。TCP 不允许发送超过该窗口大小的数据。若窗口为 0,则表示可以发送窗口探测,以了解最新的窗口大小,但这个数据必须是 1 个字节;
检验和:TCP 的检验和与 UDP 检验和一样,也是采用伪首部,但是 TCP 的检验和无法关闭。TCP 伪首部的信息和 UDP 一样,包括:源 IP 地址、目的 IP 地址、填充、协议号以及 TCP 包长度;
紧急指针:该字段为 16 位。只有在 URG 控制位为 1 时有效。该字段的数值表示本报文段中紧急数据的指针。从数据部分的首位到紧急指针所在的位置为止是紧急数据。因此,紧急指针是指出了紧急数据的末尾在报文段中的位置;
TCP头部代码封装:
TCP状态变迁图:
二、TCP阶段说明
1.三次握手(建立连接)
2.数据传输
3.四次挥手(断开)
三、三次握手
1.三次握手示意图
2、图解析说明
(1)三次握手由客户端调用connect发起,此时发的tcp包,tcp头部中的SYN位为1.seqnum1的值为1。
(2)server端此时处于listen函数,在收到syn包后,发送ack包。ack包包含两部分:第一部分是对客户端发送的syn包的确认(seqnum1的值加1),另外一部分是syn,中包含seqnum2的值。
(3)客户端收到ack+syn后,返回给服务器端ack(seqbnum2+1)。
(4)服务端调用accept函数。(accept不参与真正意义的连接,accept只是从sync队列中拿去一个tcp控制块(tcb), 是移动,而不是拷贝,另外做的一件事情是accept为客户端分配一个fd)
说明:seqnum1客户端包序号,seqnum2服务端包序号,请求中会携带窗口大小
窗口大小:指缓冲区大小
3.其他说明
(1)SYN队列叫半连接队列(全局的队列)
(2)accept队列叫全连接队列(全局的队列)
(3)int listen(int sockfd, int backlog); 中的backlog参数的意义,在Linux中指的是SYN队列的大小。backlog不适合太小,也不适合太大,15比较合适。
(4)DDOC的处理:频繁的发起连接,两种方法处理,防火墙设置或者backlog调小,
(5)三次握手重发没意义
(6)超时如何计算?主要是通过RTT来设定超时时间的,三次握手已经计算了超时时间
RTT= rtt0; //三次握手计算
RTT1=rtt1;
RTTn=RTT(n-1)*0.1 = oldrtt*0.9, 此公式主要的目的是为了解决:RTT值变化造成的抖动。
(7)三次握手为空数据包
四、数据传输
1、数据包长如何计算
TCP头部协议里并没有数据包长,是通过IP包头里的总长度计算来的。
2.1M数据(块数据)传输过程
3.块数据传输解析
阶段介绍:
时间0~4为慢启动,第一次,初始化为1个包,之后以指数形式固定增长发包。
时间4~7为拥塞控制,以线性固定增长的方式发包
切换:
慢启动到拥塞控制:固定的预值16,当达到16的时候,固定为拥塞控制阶段,
拥塞控制阶段描述:
当接收方回ACK包的时间没超过RTT的时,发包个数以线性增长。
当接收方回ACK包的时间超过RTT的时间,这时网络进入拥塞,此时发送方会把上一次的发包个数除以2的值作为下次发送包的个数,继续发送,循环往复。如下图(很糙)
4.慢启动
慢启动为发送方的T C P增加了另一个窗口:拥塞窗口 (congestion window),记为c w n d。当与另一个网络的主机建立 T C P连接时,拥塞窗口被初始化为 1个报文段(即另一端通告的报文段大小)。每收到一个 A C K,拥塞窗口就增加一个报文段( c w n d以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。
发送方开始时发送一个报文段,然后等待 A C K。当收到该A C K时,拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的 A C K时,拥塞窗口就增加为 4。这是一种指数增加的关系。
简单理解:慢启动是以指数的形式增长发送包的个数,第一次1个,第二次2个,第三次4个,第四次8个,第五次16个,当达到16个,进入拥塞控制阶段。
5.窗口大小
在三次握手的时候,会携带初始窗口大小
在发送数据的时候,对方会ACK的过程中,也会携带窗口大小
修改:修改sysctl.conf里,修改rmem的大小
6.滑动窗口
解析:
滑动窗口首先第一个概念:窗口可以放大和缩小,也可以固定的平衡移动
滑动窗口主要包含以上四部分的数据包,接收端和发送端有区别
举例:
开始的窗口大小为3到20,
发送端发送了。3/4/5/6/7/8/9/10几个包,但是接收端,没有接到到8号包,然后超时回复ack从8号包开始重传,窗口向后移动,这时候窗口变为8到25,叫滑动窗口,随着发包或者受包而改变的。
7.超时重传
接收端: 接收到一个包充值定时器为40ms,图中为当8号包没到,剩下的5个包已经到了,当最后一个包到了,重置定时器,当到时见了,重排发现8号没到,就给发送端回应一个ack,序号为8的包,表示,从8号包以后的包重新发送。
发送端:发送端在发送了数据之后启动定时器,当在RTT时间范围内没有收到响应,进行重传。
8.如果窗口大小固定不变
数据发送过程
前半段时间,慢启动,当达到预制,固定包的数量发送
五、四次分手
说明:
四次分手client和server都可以发起。
(1)主动方发起的时候,把fin位置1,此时发起方处于写关闭状态(应用层不能超内核拷贝业务数据)。
(2)被动方收到fin之后,回复对应的ack,此时关闭读端(应用层不能从内核拷贝业务数据),紧接着被动方调用recv = 0返回,调用close,发送fin位为1的包,此时关闭写段(应用层不能超内核拷贝业务数据)。
(3)当主动方收到ack后,关闭读端(应用层不能从内核拷贝业务数据),在收到fin之后,发送ack,然后进入time_wait状态。
1、状态
(1)send之后如何判断对端是否接收成功?
send的返回值只是把数据拷贝到了内核态,不是真正意义上的发送
(2)分包的原因?
是由于send发了大包造成的
(3)粘包处理?
原因:比如第一次send了32个字节,第二次send了64个字节,发送过于平凡,内核把两个包放到一起发送,
解决:a.应用层协议增加包头,b.应用层协议结束加换行符,c.发送定长数据包(不建议使用)
(4)send返回-1?
结果是send返回-1,error=egain, 表示内核发送缓冲区已满
(5)多个线程同时调用一个fd,同时send,使得send_buf缓冲区数据混乱解决?
a.引入多路复用epoll、poll、select
b.加入epoll判断fd是否可以写,如果可以写,把fd从监控队列中移除,然后发送数据,然后在把在epoll监控队列中把fd的监控重新加入写监控。
(6)recv =-1, fd非阻塞,recv_buf没有数据
(7)fin包可以单独发送,也可以和其他包一起发送
(8)一方调用了close,接收方要不要处理业务数据?
需要处理,数据会持续接受完,才会处理fin数据包
(9)如果发送了10包数据,然后调用了close,接收端丢了5包数据?
接收端会想用丢包ACK,发送端重新传数据,再重新发送fin
(10)如果出现大量的close_wait状态?
使用:netstart -anop | grep 端口号 查看
原因:被动方处理了业务消息,没有及时调用close
处理:a.判断有没有调用close,b.把业务数据和close做异步处理,c,先close在处理业务数据
(11)主动方出现了fin_wait_2如何接?
a.对方先处理
b.本端无解,智能kill进程
c.重新connect
(12)time_wait作用?
定时,保证对端接收到ack
如果没有time_wait,进入死锁状态
定时释放tcb块
SO_REUSEADDR把处于time_wait的fd对应的控制块不释放,当有socket在次连接的时候,拿来重用。
(13)两端同时调用clise
双方都会出现time_wait状态,定时释放
六、TCP定时器
1.超时重传RTT定时器
2.坚持定时器(探测定时器),当对端发送窗口等于零时,启动,探测对端的窗口大小,当窗口大于0时,开始发送数据
3.keeplive(保活定时器), tcp心跳包,不建议使用
4.time_wait定时器:四次挥手,保证对端可以收到ACK,断开连接,
七、TCP的设计缺点
TCP的设计缺点是针对UDP而言的
1.重传的数据太多
2.实时性不强
八、TCP中的一些状态说明
1.ESTABLISHED状态
ESTABLISHED的意思是建立连接。表示两台机器正在通信。
2.CLOSE_WAIT
对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭
3.TIME_WAIT
主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT。TCP协议规定TIME_WAIT状态会一直持续2MSL(即两倍的分段最大生存期),以此来确保旧的连接状态不会对新连接产生影响。处于TIME_WAIT状态的连接占用的资源不会被内核释放,所以作为服务器,在可能的情况下,尽量不要主动断开连接,以减少TIME_WAIT状态造成的资源浪费。
九、设置TIME_WAIT状态的目的
TIME_WAIT状态是T C P协议中最容易被误解的特性之一。设置TIME_WAIT状态的原因主要有两个:
- 它实现了全双工的连接关闭。
- 它使过时的重复报文段作废
1.TCP全双工关闭
由于没有收到客户的最后一个确认,服务器会超时,并重传最后一个 F I N报文段。我们特意把服务器的重传超时( RTO )给得比图4 - 4中的RT T大,这是因为RTO的取值是估计的RT T值加上若干倍的RT T方差。处理最后一个F I N报文段丢失的方法也是一样:服务器在超时后继续重传 F I N。
(1)为什么 TIME_WAIT状态要出现在执行主动关闭的一端
该端发出最后一个A C K报文段,而如果这个 A C K丢失或是最后一个 F I N丢失了,那么另一端将超时并重传最后的F I N报文段。因此,在主动关闭的一端保留连接的状态信息,这样它才能在需要的时候重传最后的确认报文段;否则,它收到最后的 F I N报文段后就无法重传最后一个 A C K,而只能发出R S T报文段,从而造成虚假的错误信息。
(2)如果重传的 F I N报文段在客户端主机仍处于 TIME_WAIT状态的时候到达
那么不仅仅最后一个 A C K会重传,而且TTIME_WAIT状态也重新开始。这时,TIME_WAIT状态的持续时间定时器重置为 2倍的报文段最大生存时间,即 2 M S L。
2.为让过时的重复报文段失效
T C P协议的运行基于一个基本的假设,即:互连网上的每一个 I P数据报都有一个有限的生存期限,这个期限值是由I P首部的T T L (生存时间 )字段决定的。每一台路由器在转发 I P数据报时都要将其 T T L值减1;但如果该 I P数据报在路由器中等待的时间超过 1秒,那就要把 T T L的值减去等待的时间。实际上,很少有I P数据报在路由器中的等待时间超过 1秒的,因而每个路由器通常都是把 T T L的值减1(RFC 1812 [Baker 1995])的5 . 3 . 1节)。由于T T L字段的长度是8比特,因此每个I P数据报所能经历的转发次数至多为 2 5 5。
由于该连接的新的替身必须在前一个连接替身关闭 2 M S L之后才能再次发起,而且由于前一个连接替身的过时重复报文段在 T I M E _ WA I T状态的第1个报文段最大生存时间里就已经消失,因此我们可以保证前一次连接的过时重复报文段不会在新的连接中出现,也就不可能被误认为是第二次连接的报文段。
3、TIME_WAIT状态的自结束
处于T I M E _ WA I T状态的连接在收到 R S T后变迁到C L O S E D状态,这称为T I M E _ WA I T状态的自结束