TCP详解
零声学院TCP/IP训练营笔记。
协议头
TCP的头部结构为: ??(1) 16位的源端口号/目的端口号 ??告知目的机器报文段来自哪里(源端口号)以及传给传递给哪个上层协议或者应用程序(目的端口号)。进行TCP通讯时,客户端通常使用系统自动选择的临时端口号,而服务器则使用知名端口号。在Linux系统中,一些知名服务使用的端口号定义在/etc/services中。 ??(2) 32位序号 ??一次TCP通讯(从TCP连接的建立到断开)整个过程中,一个传输方向上的字节流的每一个报文的编号。例如主机A和主机B进行TCP通讯,A发送给B的第一个TCP报文中,序号值就被系统设置为某个随机值(ISN, Initial Sequence Number),在该传输方向(A->B)的后续TCP报文的序号子将被系统设置为ISN加上该报文所携带的第一个字节在整个字节流的偏移。假设某个TCP报文段传输的数据是整个字节流中的第1024~2048字节,那么该报文的序号值为ISN+1025,下一个报文为ISN+2049。 ??(3) 32位的确认号 ??用于对对方发来的TCP报文段的响应,其值为收到的TCP报文段的序号值加1。 ??(4) 4位头部长度 ??标志该TCP头部具有多长,单位为字(4字节),可见TCP头部最长为60字节。 ??(5) 6位保留 ??(6) 6个标志位 ??① URG: 表示紧急指针是否有效 ??② ACK: 表示确认号是否有效(携带ACK标志的TCP报文段称为确认报文段) ??③ PSH: 提示接收端应用程序要立即从TCP接收缓冲区读走数据,以腾出空间接收后续的数据。(若应用程序不读走数据,数据会一直留在TCP模块的接收缓冲区) ??④ RST: 表示要求对方重新建立连接(携带RST标志的TCP报文段为复位报文段) ??⑤ SYN: 表示请求建立一个连接(携带SYN标志的TCP报文段称为同步报文段) ??⑥ FIN: 表示通知对方要关闭连接(携带FIN标志的TCP报文段为结束报文段) ??(7) 16位窗口大小 ??这是TCP流量控制的一个手段。此处的窗口指的是接收通告窗口,用于告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。 ??(8) 16位校验和 ??由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。作为TCP可靠传输的重要保障,这个校验不仅包括TCP头部,也包括数据部分。 ??(9) 16位紧急指针 存放着一个正的偏移值,该值加上当前报文的序号将得到紧急指针,紧急指针处存放的是紧急数据,是发送端向接收端发送紧急数据的方法。 参考
#page
struct tcphdr {
unsigned short sport;
unsigned short dport;
unsigned int seqnum;
unsigned int acknum;
unsigned char hdrlen:4,
resv:4;
unsigned char cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
unsigned short wsize;
unsigned short tcpcheck;
unsigned short urg_pointer;
unsigned char option[0];
}
三次握手/四次挥手,11种状态迁移
TCP三次握手状态图:
服务器使用tcb控制块保存客户端的握手信息。 客户端第一次发送SYN包,服务端会将其五元组(源IP地址,目的IP地址,源端口,目的端口,协议号)存放到半连接队列,listen(fd, backlog(半连接队列+全连接队列))。第三次握手会从半连接队列中找到对应的五元组放入全连接队列等待accept。
四次挥手状态图:
如果客户端和服务端同时调用close,那么就会进入CLOSING状态。 close_wait:被动关闭的一方长时间未调用close; time_wait:ack丢失。 1.ack(正常流程) --> fin FIN_WAIT2 2.fin(同时close) --> ack CLOSING 3.ack+fin(合在一起发) --> time_wait
TCP11种状态迁移图:
tcp的顺序,如何保证有序,延迟ack/滑动窗口
通过包的序号和延迟确认ack来保证包的顺序。
tcp滑动窗口协议
TCP的滑动窗口用来控制接收方和发送方的发送速率,避免拥塞的发生。滑动窗口其实就是接收端的缓冲区大小,用来告诉发送方对它发送的数据有多大的缓冲空间。在接收方的滑动窗口已知的情况下,当接收方确认了连续的数据序列之后,发送方的滑动窗口向后滑动,发送下一个数据序列。 接收方会在每个ACK数据包中附带自己当前的接受窗口(滑动窗口)的大小,方便发送方进行控制
定时器,rtt
从三次握手开始记录下rrt初始值,超时时间进行削抖, rtt = 0.9 * old_rtt + 0.1 * new_rtt;
慢启动与拥塞控制
防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载,拥塞控制自然也是控制发送者的流量,拥塞控制有四种算法,慢启动、拥塞避免,快速重传和快速恢复发送方维持一个拥塞窗口 cwnd ( congestion window )的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口和接受窗口的较小值。
慢启动。
慢启动算法的思路是当主机开始发送数据时,先以比较小的拥塞窗口进行发送,然后每次翻倍,也就是说,由小到大逐渐增加拥塞窗口的大小,而这个大小是指数增长的,即1、2、4、8、16*为了防止拥塞窗口cwnd增长过大引起网络拥塞,还要另外设置一个慢启动阈值ssthresh状态变量,当拥塞窗口的大小超过慢启动阈值的时候( cwnd > ssthresh 时),停止使用慢开始算法而改用拥塞避免算法
拥塞避免。
拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。
快速重传。
当发送端连续收到三个重复的ack时,表示该数据段已经丢失,需要重发。此时慢启动阈值ssth变为原来一半,拥塞窗口cwnd变为ssth+3,然后+1+1的发(每一轮rtt+1)
快速恢复。
当超过设定的时间没有收到某个报文段的ack时,表示网络拥塞,慢启动阈值ssth变为原来一半,拥塞窗口cwnd=1,进入慢启动阶段
流量控制的介绍,采用滑动窗口会有什么问题(死锁可能,糊涂窗口综合征)?
所谓流量控制就是让发送方发送速率不要过快,让接收方来得及接收。利用TCP报文段中的窗口大小字段来控制发送方的发送窗口不大于接收方发回的窗口大小就可以实施流量控制。 考虑一种特殊的情况,就是接收方若没有缓存足够使用,就会发送零窗口大小的报文,此时发送放将发送窗口设置为0,停止发送数据。之后接收方有足够的缓存,发送了非零窗口大小的报文,但是这个报文在中途丢失的,那么发送方的发送窗口就一直为零导致死锁。 解决这个问题,TCP为每一个连接设置一个持续计时器(persistence timer)。只要TCP的一方收到对方的零窗口通知,就启动该计时器,周期性的发送一个零窗口探测报文段。对方就在确认这个报文的时候给出现在的窗口大小(注意:TCP规定,即使设置为零窗口,也必须接收以下几种报文段:零窗口探测报文段、确认报文段和携带紧急数据的报文段)。0 参考
|