4-4:TCP协议之TCP头部格式详解
Posted 快乐江湖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4-4:TCP协议之TCP头部格式详解相关的知识,希望对你有一定的参考价值。
文章目录
一:TCP头部格式详解
(1)4位首部长度
标准TCP报头宽度为4个字节,总计4×5=20个字节,所以在收到一个TCP报头后首先会读取20个字节(也就是报头信息)
报头中的选项是可有也可以没有的,关键就取决于4位首部长度。它表示该TCP头部有多少个32位bit(也就是有多少个4字节)。这4位是一个无符号数,取值范围为0000~1111,也即是0到15,当为1111时表示该TCP报头有15个4字节(60字节),而标准长度是20字节,所以选项最多是40字节
4位首部长度不可能是全0,因为TCP报头长度至少是20字节,也就是至少是20/4=5,对应二进制序列为0101
于是TCP分离有效载荷和报头时,拿到TCP报文后先直接读取20字节,然后拿出4位首部长度进行分析,如果就是5那么剩下的就是数据,如果不是5,再读取相应字节数即可
**TCP和UDP不一样,UDP中有包长度来确定数据的长度,在TCP中是不需要包长度的,因为TCP并不需要按照数据块交付,是面向字节流的,怎么读,读多少是应用层协议需要做的事情,而它只负责将其读入缓冲区中。**报文的完整性依靠校验和完成
(2)序列号和确认应答号
A:可靠性问题
可靠性涉及很多层面,比如数据有序,准确等等。但是这一切一切都要有一个前提,那么就是如何保证数据对方收到?
假设有两个人A和B隔着很远的距离喊话。A首先向B喊:**B你听到了没有?**如果此时A没有听到B的回应,那么对于A来说他一般会有两种感觉:我说的话你没有听见或者你说的话我没有听见, 显而易见对于A来说他更倾向于第一种感觉,所以A在没有得到反馈时他一定会再次喊话
那什么时候A能确认B已经收到了自己的信息呢?当然是B的收到信息反馈给了A,也就是说这种反馈信息只是对A是有价值的,A通过B的反馈信息确认了我的信息已经传达到了。
但是这里又产生了新的问题,对于A来说它得到了B的反馈确认了自己的信息已经递达,但是对于B呢,B在发出反馈信息时,对于B他如何确认自己的反馈信息也传达到了A那里了呢?,显然A也发送反馈信息给B,如果B收到了那么B也就确认了。但是这里往返传递,总会有一条消息处于未决状态,总有一条消息无法得到收到反馈,最后一条消息无法保证可靠性
所以互联网通信中是不可能有绝对的可靠性的,只能保证相对的可靠性,也就是这种相对可靠性指的是,只要收到了对方的应答,就认为之前的数据对方已经收到
于是我们说:TCP基于的是确认应答机制
在TCP三次握手中,客户端发送请求号,服务端给客户端的反馈就是图中的ACK。
如果ACK在传输途中丢了,如果服务端长时间没有接受到客户端回应时,就会触发重传机制,进行重传。
这里有一个很大的误区:我们不需要对ACK再ACK,因为一旦你对ACK进行ACK了,那么你就需要对ACK的ACK再进行ACK,这是一个无休无止的过程。确认应答并不是对ACK保证可靠性,而是通过ACK保证上一次发的消息时可靠的
B:32位序号和确认号
网络传输毕竟是远距离传输,所以会受到各种因素的影响,在传输过程中极有可能出现丢包的现象发生。而如果包都能达到,有可能会涉及到的乱序的问题,所以如果保证数据按序到达也是可靠性的一种体现
32位序号
假如有三段TCP报文,需要按照以下顺序发送给服务端
- 你好
- 在吗
- 去吃饭
如果TCP没有保证的有序的机制,那么这三段报文到达服务端的顺序可能会多种多样,比如
- 去吃饭
- 你好
- 在吗
TCP中的32位序号可以保证数据有序到达。如果数据由客户端发送给服务端,那么32位序号就由客户端填写,它保证的就是客户端的数据有序到达服务端(反之亦然)。客户端在发送时会为发送的数据报文进行编号,比如
- 你好
- 在吗
- 去吃饭
服务端收到之后即便是乱序的,它也可以根据编号进行排序
32位确认号
上面的三条数据:1“你好”,2“在吗”,3“去吃饭”在客户端发送出去后,服务端接受会给三个ACK,分别为ACK1,ACK2,ACK3。那么这里存在一个问题,客户端如何确认现在到来的ACK是对我发出的哪一条数据的确认反馈呢?,如果其中有一个ACK丢了,那还不乱套了。
32位确认序号可以保证得到的ACK是相应报文的反馈。举个例子,客户端个发送的顺序为1,2,3,如果客户端此时收到的ACK中的确认序号是2,那么对于客户端来说,它就认为它之前发送的1号已经收到了,现在应该从2开始发,如果受到的ACK确认序号是9,那么表示2也收到了。
所以如果发送顺序为1,2,3,那么ACK顺序则应该为2,3,4。但如果确认序号中2,3丢了,只收到了4,对于客户端来说它就会认为3之前的数据已经全部收到。所以这就代表TCP可以少量丢包
32位序号和确认序号为什么要同时存在
需要注意的是TCP是全双工的。客户端给服务端发送时,服务端关心的就是序号,客户端关心的确认序号。但同时服务端也会给客户端发送数据,所以同时客户端也会关心序号,而服务端也会关心确认序号
(3)窗口大小
窗口大小是指发送方接受缓冲区中剩余空间的容量,用于流量控制。
- 如下,接受和发送缓冲区
因为窗口大小的目的就是要让对方明白我这个接收端现在的接受缓冲区容量的大小,让发送端看实际情况发送。不要造成我都快满了,对方还硬要发的局面(虽然有满了之后有重传机制,但是这种占用网络资源的行为是一种设计问题)
(4)标志位
TCP功能非常强大,有建立连接的、有关闭连接的,还有一些正常数据报文等等。那么双方是如何区分不同功能的TCP报文呢?正是通过其中的标志位,如果某个标志位被设置为1,表示该标志位生效。
SYN
当SYN为1时,表示希望建立连接,并在其【序列号】的字段1进行序列号初始值的设定
FIN
当该位为1时,表示今后不再会有数据发送,希望断开连接。当通信结束希望断开连接时,通信双发的主机可以相互交换FIN位为1的TCP段
ACK
当该位位1时,【确认应答】字段变为有效,TCP规定除了最初建立连接时的SYN包之外该位必须设置为1
PSH
当该位设置为1时,表示发送端催促接收端尽管向应用层交付数据,及时清空缓冲区,以便后续数据到来
RST
当该位设置为1时,表示TCP连接中出现异常必须强制断开连接。
URG
当该位设置为1时,表示TCP报文中发送的数据含有紧急数据。也就是16位紧急指针只有当URG为1时才有效
(5)紧急指针
A:带外数据(out_of _band)
传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方.为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道
如何理解呢?你可以想象一个场景:银行处理业务时人们需要排队,这时进来了一个强盗,他摇摇晃晃,大摇大摆排队直接走到了银行职员前面,为什么他这么放肆呢?因为他手里有一把枪,最后他用抢抵着职员的脑袋逼迫她先为自己办理业务。
此时这个强盗就可以看作一个带外数据,而职员也一定会优先处理他,因为它明白此时情况很紧急。
B:URG
linux系统的套接字机制支持低层协议发送和接受带外数据.但是TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP提供了一种称为紧急模式(urgentmode)的机制.TCP协议在数据段中设置URG位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理.很容易看出来,这种方式数据不容易被阻塞,可以通过在我们的服务器端程序里面捕捉SIGURG信号来及时接受数据或者使用带OOB标志的recv函数来接受.
(6)校验和
校验和失败,丢弃数据。触发重传
- 比如发送顺序报文1,2,3,4,5,其中3号报文校验和失败,那么确认应答就是3,表示从3继续开始发。
二:Linux中的TCP实现
Linux中TCP的报头本质就是一个结构体
struct tcphdr {
__be16 source;//源端口号
__be16 dest;//目的端口号
__be32 seq;//序号
__be32 ack_seq;//确认序号
#if defined(__LITTLE_ENDIAN_BITFIELD)//标志位采用位段实现
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;//窗口大小
__sum16 check;//校验和
__be16 urg_ptr;//紧急指针
};
以上是关于4-4:TCP协议之TCP头部格式详解的主要内容,如果未能解决你的问题,请参考以下文章