文章目录
- 一.TCP是什么
- 二.TCP协议格式
- 1.报头属性解释
- TCP首部长度/如何解包分用
- 三.确认应答机制-tcp如何保证可靠性
- 1.确认应答机制
- 2.序号/确认序号-如何保证报文按序到达
- 3.为什么要两个序号
- 四.16位窗口大小-调整发送策略
- 五.6个标志位
一.TCP是什么
首先我们需要知道TCP是什么,TCP全称是传输控制协议(Transmission Control Protocol),它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
网络传输,无非就是要做两件事情,一是做决策,二是做执行,在网络传输中,传输层主要做的是决策,下面的层做执行。所以TCP的任务就是做决策,决定怎么样去发数据,怎么发,什么时候发,出错了怎么办等一系列的问题。
二.TCP协议格式
1.报头属性解释
首先大致说一下报文中各个属性的作用
-
源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;
-
32位序号/32位确认序号: 见下方的 三.2
- 序号用来保证当前携带数据能够按序被对方接受(收到后排序);
- 确认序号用来告诉对方,收到了对方当前确认序号之前的所有报文。
-
4位TCP报头长度(单位是4字节): 表示该TCP报头有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60字节(毕竟4位最大是15),见2)TCP首部长度/如何解包分用
TCP报头长度最小是20个字节,一般都是20个字节。最大长度是60位。 -
6位标志位:见下方的 5.6个标志位
-
16位窗口大小: 见下方四
-
16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分.
-
16位紧急指针: 标识哪部分数据是紧急数据;(见五.URG标志位)
-
40字节头部选项: 这里暂时不做解释
TCP首部长度/如何解包分用
TCP标准长度是20个字节(这里有5行,一行4字节,最少就是20个字节),所以一般来说,4位表示TCP首部的长度一般是0101(5),表示20个字节(单位是4字节)
Q1:如何解包?从前20个字节中获取到4位首部长度,从而确定首部占多少字节,然后分离首部和有效载荷即可。
Q2:如何分用?tcp的报头中,也存在16位的目的端口号,所以根据目的端口号,交付给上层对应的应用进程。
三.确认应答机制-tcp如何保证可靠性
1.确认应答机制
1.TCP保证可靠性最核心的机制:基于序号的确认应答机制(没有这个机制,其他的机制都无法成立。)
2. 这个机制就是通过应答,来保证上一条消息100%被对方收到了。(核心意义:我收到应答,那就说明上一条消息对方收到了)(收到报文要返回应答并不是核心意义)
3.我收到应答以后,要给对方应答,让对方知道他的应答我收到了;对方收到以后又要应答,也就是应答的应答…所以最后总会有最新的一条消息,是没有应答的。因此tcp并不是100%可靠的。
4.所谓的可靠性:只要一条消息有应答,我们就能确认该消息被对方100%收到了。
结论:
1.互联网长距离传输中,没有百分百可靠的协议。
2.只要一条消息有应答,我们就能确认该消息被对方100%收到了
e.g.
如果client给server发消息,
1.client向server发送数据,server收到数据后,给client发送一个应答。
2.client收到应答后,对于client来说,就可以保证这一次传输的可靠性,也就是client知道server收到了自己发送的消息。
tips:虽然server无法确定自己的应答有没有被对方收到,但是无所谓,因为是client发送的数据,client知道sever收到自己发的消息就可以了。
如果server给client发消息,同理。
结论:基于确认和应答,双方每次收到数据的时候都给出应答,对方收到应答,这样就能保证双方发送的历史数据都被收到(除了最新的那条),这种可靠性是100%的。
2.序号/确认序号-如何保证报文按序到达
当client给server发送多个报文的时候(1,2,3,4,5,),server端针对每个报文,给出5个应答响应。client收到响应后,知道自己的报文被server收到了
但是,client如何确保server是否是按照1,2,3,4,5的顺序收到了呢? (毕竟网络的环境是很复杂的,可能发1的时候慢一点,5快一点)
tcp的可靠性除了保证对方能收到,同时也要保证按序到达。
32位序号的作用就是保证按序到达,给每个报文按发送顺序编号,这样收到以后根据序号排序即可。
那server对client发送的报文进行应答的时候,发送给client 5个应答,那client如何知道这5个应答分别对应client发送的哪个报文呢?
使用确认序号,tcp报头中涵盖一个叫做确认序号,是对历史确认报文的序号+1
1.比如server要进行应答,应答的时候确认序号为13,表示1-12的数据报都已经收到了。(这也就是为什么确认序号是序号+1,这样client收到以后就直接从确认序号对应的报文开始发送),此时client发现确认序号为13,下次发送请从13号报文开始发送
2.即client收到确认应答tcp报文之后,可以通过确认序号,来辨别是对哪一个报文的确认
3.为什么要两个序号
我在了解这块只是的时候有个疑问,为什么报文中需要有两个序号,每次传递实际上只用到了一个序号啊(发送的时候填序号,应答的时候填确认序号)
后来查阅资料得到结论:tcp是全双工的通信协议,就是我在给你发的时候,你也在给我发。(因为我们前面考虑的情况只是client给server发送数据,是单方向的,所以自然只需要使用一个序号)
双方通信的时候,一个报文,既可以携带要发送的数据,也可能携带对历史报文的确认
client给server发送消息,server给client应答的时候,除了填上确认序号表示收到client发送的确认序号之前的所有报文,还要发送一些数据给client,比如我收到了,那你要这样处理等等,这时候携带了数据,为了保证client端收到数据的有序性,那就要填上序号了。
这就是一种全双工的通信。(我在给你发的时候,你也在给我发)
结论:当一个报文中既要携带数据,又要携带对历史报文的确认的时候,报文中就同时需要序号和确认序号。
- 序号用来保证当前携带数据能够按序被对方接受(收到后排序);
- 确认序号用来告诉对方,收到了对方当前确认序号之前的所有报文。
四.16位窗口大小-调整发送策略
首先我们来了解一下tcp的发送缓冲区和接收缓冲区。
1.TCP有自己的发送和接收缓冲区,就是两段内存空间。
2.TCP属于传输层,和应用层之间隔着一层系统调用。
当我们在应用层调用write,send进行网络通信的时候,与其说是发送数据的函数,不如理解为拷贝函数,这两个函数并不是将数据直接发送到网络上,而是把数据拷贝到TCP的发送缓冲区。
当我们调用read或者recv的时候,也是去接收缓冲区里面读取数据的。
注意:UDP没有发送缓冲区,write和send直接把数据交给内核,由内核把数据交给网络层传输;TCP有发送缓冲区,write和send是把数据拷贝到TCP的发送缓冲区,然后根据TCP的发送策略对数据进行发送。
那么我们为什么要这样一个缓冲区呢?
1.因为我们调用write将数据发送到网络中,实际上是使用了网卡,是一个外设,是一种IO,相当于我们把数据从内存刷新到了外设/磁盘,IO都是比较慢的,如果没有缓冲区,应用层调用write的时候可能会因为IO阻塞住(用网络传输数据也是IO) 。如果有缓冲区,应用层就只需要把数据往发送缓冲区里面拷贝就可以了,可以减少阻塞,提高应用层的效率。
2.只有OS的 TCP协议可以知道网络,乃至对方的网络状态明细,所以,也有只有TCP协议,能处理如何发,什么时候发,发多少,出错了怎么办等细节问题。应用层只需要把数据交给我传输层就行,具体什么时候发,发多少,出错了怎么办等细节问题交给我传输层来控制,tcp协议也因此得名-传输控制协议(Transmission Control Protocol)因为缓冲区的存在,所以可以做到应用层和TCP进行解耦!(你应用层拷贝你的,我传输层传输我的)
因此我们可以得出结论:1.提高应用层效率;2.将应用层和TCP进行解耦。
16位窗口大小的作用
采用tcp协议,当发送方给接收方发送报文的时候,如果没有任何控制或者策略,可能会导致接收方的接收缓冲区满了,导致报文丢失。虽然有超时重传的机制,但是高低还是浪费了网络资源。
接收方收到报文后,要进行应答。可以在应答报文中,填上自己接收缓冲区的剩余空间大小(在传输层),这个数就填在16位窗口大小中。
这样子发送方就可以知道当前接收方缓冲区的剩余空间大小,根据接收方的接收能力动态调整发送策略。
流量控制:让发送方发送数据处于一个合理的速度(不要太快也不要太慢)
五.6个标志位
首先我们要知道为什么要有这6个标志位,server可能在任何一个时刻,都有可能有成百上千个报文再向server发送数据。面对大量的TCP报文,对不同类型的报文要进行不同的处理,如何区分各个报文的类别?——六个标志位,每个标志位是一个比特位,要不是1要不是0
1.ACK(acknowledgement):表明当前报文是确认报文,确认序号为之前收到报文的序号+1,是用于对之前的报文做确认的。几乎在所有的TCP通信过程中,ACK都会被设置。(简单说就是当前的确认序号是有效的)
2.SYN(synchronous):同步标志位,也叫作建立连接请求的标志位,表示当前报文是申请建立连接的。发送方发送SYN,表示要与接收方建立连接。(详情见下方三次握手大致流程的第1点)
3.RST(reset):用于重置异常链接的。告诉对方链接失败了,让对方关闭当前连接,重新连接(比如发送方认为链接建立成功,接收方并没有确认链接建立成功,此时发送方发送数据给接收方,接收方就发送一个RST报文,表示当前链接异常,让发送方重置链接,就是关闭当前链接,重新尝试链接)----(详见下方(6)-2))
4.PSH(push):用于催促对方的应用程序赶紧把数据从TCP接收缓冲区中读走。(发送方根据接收方返回的报文的窗口大小得知,接收方的tcp接收缓冲区快满了,于是发送一个PSH报文,让接收方上层的应用程序赶紧从tcp接收缓冲区中读取数据)
并不是说PSH一次,对方就会马上读,如果读的太频繁,会导致一些不必要的系统开销,而且有些数据不是想读就可以读的,OS还是希望让应用程序一次读一批数据,PSH只是通知应用程序赶紧可以读了。
5.URG(urgent):因为TCP能保证报文按序到达,每一个报文,什么时候被上层读取到基本是确定的。如果想让一个数据尽快的被上层读到,可以设置URG为1,表明该报文中携带了紧急数据,需要被优先处理(URG为1)。
紧急数据存放在tcp携带的数据中,具体是哪一部分由tcp报头中的16位紧急指针来指出。这个紧急指针只能传输1个字节的数据。
如何使用?send/recv函数中有一个flag,可以设置为MSG_OOB,表示读取紧急指针,此时会优先读取紧急数据。(也称为带外数据)
6.FIN(finish):表示请求与对方断开连接