文章目录
- 1.引入
- 2.协议段格式
- 4位首部长度
- 16位窗口大小
- 32位序号
- 思考三个问题【demo】
- 标记位
- URG: 紧急指针是否有效==提升某报文被处理优先级【0表示不设置1表示设置】
- ACK: 确认号是否有效
- PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
- RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
- SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
- FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
- 总结标记位
- 总结报头属性
1.引入
之前讲的网络版计算机/http协议 都是基于tcp协议 哪里体现出来?我们用了tcp套接字编程及三次握手等。
TCP协议
TCP全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制;
用户级缓冲区
自己定义的string/char buffer
write/read/recv/send --》不是发送函数 而是拷贝函数
这些接口本质不是把数据发送到网络中 而是把数据从用户级缓冲区拷贝到发送缓冲区里。发送缓冲区里的数据什么时候发送,发送多少,出错了怎么办 — 由OS的TCP协议自主决定
似曾相识?
讲文件时,我们提到 把自己的charBuf的数据调用write接口”写“到文件里 实际上write是把用户级缓冲区的数据拷贝到文件缓冲区(每个文件都有自己的文件缓冲区)刷新策略由OS控制!把磁盘换成网卡,是不是就理解了?!文件读写属于IO,网络也一样!在用户层做:报头和有效载荷分离,序列反序列,把这些数据调用write接口拷贝到发送缓冲区,之后就由OS来管理了!
read读文件/接收缓冲区 阻塞
接收缓冲区没有数据 阻塞等待 将read进程挂起 当接收缓冲区有数据(OS内某些条件就绪了) 再调度。
一个文件可以同时读写?
一个文件描述符配备两个缓冲区(发送/接收)
发送/接收缓冲区实际上是内存空间
OS为了管理内存 把他们划分为4kb内存块 哪些内存块被占用/被系统锁住(不能换入换出)/ 需要刷新?管理!–每一个内存块都有一个struct page{};这里的发送接收缓冲区实际上是很多个struct page管理的内存块。对内存的管理变成对结构体数组的增删查改!打开网络–打开一个文件–文件描述符–struct file{}–本应指向磁盘 现在让他指向网络设备 再加上两个缓冲区–网络IO!
传输控制协议的控制体现在哪里?
数据从用户级缓冲区到发送缓冲区后 什么时候发到网络 发多少 出错怎么办 由TCP协议自主决定 – 控制。udp没有发送缓冲区 自然无法做到控制!udp一直发 接收端收不下的多于内容就丢弃了 但是tcp有”控制“,它可以让接收端尽快清空接收缓冲以便于接收新数据。
2.协议段格式
源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;
32位序号/32位确认号: 后面详细讲;
4位TCP报头长度: 表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60
16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分.
16位紧急指针: 标识哪部分数据是紧急数据
不同的名字
4位首部长度
计算时有基本单位 4B
[0000,1111] --> [0,15] —> [0,60]
如果没有选项 只有标准报头 那么 4位首部长度应该存5
报头和有效载荷如何分离?
固定长度(标准报头长度20B)+ 自定义字段(4位首部长度x)
收到一个报文 截取20B获取标准报头header 提取header的x x-20即为选项长度 再截取x-20即为选项 剩下的报文即为有效载荷!
16位窗口大小
前面学了应用层 现在在学传输层 对于整个协议栈 只考虑应用层和传输层 我们要有以下的认识
- 客户端在应用层构建了一个http请求 交付给传输层 传输层会加一个tcp报头。
- 服务端在传输层接收到会分离报头和有效载荷。
- client和server基于tcp协议进行通信互发消息的时候,发送的是完整的tcp报文,即,一定携带完整的tcp报头! 理解:http请求与响应,发送数据等都是一个完整的报文,即,只要想通过网络通信那么就要封装和分离报头。
- 一个tcp链接在应用层看 实际上就是一个fd配着两个缓冲区(发送缓冲区/接收缓冲区)。
讲可靠性 先了解什么是不可靠
数据重复/乱序/丢包
丢包:接收端的接收缓冲区已经满了 发送方并不知道还在发 丢包!怎么解决?当接收缓冲区已经满,想办法让发送方知道并发慢一点。—流量控制。
怎么做到流量控制?
tcp可靠性中有:丢包重传/超时重传。那么不做流量控制,发送方就一直发,接收方没收到发送方重传,这样可以吗?可以!但是不合理!这样会消耗资源!一个正常的数据发过来由于接收方缓冲区满而让发送方重传,不合理!消耗资源!降低效率!这种情况,重传机制并不合理!比较好的做法是让发送方发慢一点!
TCP凭什么保证可靠性?
一个基本特点:应答机制!接收方收到数据就会应答,表示自己收到了!上面已经提到 此处说的发送消息或者确认应答 只要经过网络 那么他就要带着报文!
发送方发送速度的依据?
由接收方接收缓冲区的剩余空间大小决定!发送方怎么得知?接收方的应答是一个带着tcp报头的报文,报头中【16位窗口大小】的字段就是【接收方接收缓冲区的剩余空间大小】。
【16位窗口大小】是发送方自己的接收缓冲区剩余空间大小!
CS双方通过携带这一字段 告知对方你给我发数据时要根据这个悠着点来!
32位序号
世界上不存在百分百可靠的网络协议!
- 发送方发了一条信息 收到了应答 可以确定:我最近发送的信息对方收到了!
- 没有收到应答 不能保证对方收到了!永远存在一条最新的消息 是没有应答的!
局部上存不存在百分百可靠的网络协议?
最新一条信息之前的信息都能保证是可靠的!除了最新的消息 之前的每一条信息都有应答!实际上,没有必要对应答做应答 — 死循环!
tcp最基本最原始的通信过程
发送方发了一条信息 接收方应答 就可以保证发送方发的消息被接收方成功收到了!发送方有必要对应答做应答吗?没必要!发送方不必告知接收方我收到你的应答了!发送方需要应答的目的是需要知道接收方到底收到没有,接收方不需要应答是反正我已经收到了,我不需要关心发送方知不知道我收到了!即只要保证发送方发的消息接收方收到了就行!
保证这两个方向上的可靠,即,我发送后知道你有没有收到,你发送后知道我有没有收到。
关于应答
发送方发送数据后 如果一段时间没有收到应答 认为接收方没有收到数据 重传!
接收方发的应答丢失了怎么办?同上!这也引来一个问题:发送方发送的数据发送给网络后还要在发送缓冲区里待一段时间,如果没有收到应答还得重传!如果收到应答,如果这段数据之后不用了就可以移出发送缓冲区!
优化 – 捎带应答【应答+要发的消息】
发送方一次发很多消息
发一条需要接收到应答才发下一条 … 这样是十分低效的!一次发一批!
发10条收10条应答 ---- 更真实的tcp!
tcp发的时候按顺序发 收的时候呢?
先发的不一定先收(经过网络受等多方面影响)。---- 数据包乱序! – 乱序是不可靠的一种场景!数据以乱序发给接收方这个是不可控的!怎么办?
32位序号的应用场景
发送方发送数据时 对各个报文编号 【32位序号】一个作用就是保证数据按序到达!即接收方收到后排序接收!
序号是什么?
发送方发送的数据:从用户层拷贝到发送缓冲区,然后OS把发送缓冲区里的完整数据一个个发给网络。发送缓冲区:(实际上是一个char 字符数组buf – 面向字节流,由于是数组,每个字节天然都有一个编号即数组下标),每一块数据在buf里占连续的空间,每个数据块的最后一个字符的下标就是【序号】!
一批发送一批响应 , 响应是对哪一个数据的响应?
【确认序号】!== 收到的报文序号 + 1 。意义表示:确认序号之前的数据我已经全部收到了!下次发送请从确认序号指定的数字开始发送
1001,2001发送时丢失了,但是发送方收到了3001,说明接收方在3001之前的数据都接受到了! – 允许存在应答丢失情况!、
如果接收方压根没有收到2000,但是收到了1000,3000,然后应答1001,3001,发送方收到3001是不是仍然认为接收方接收到3001前的数据,这种场景不是错的吗?
这种情况,接收方收到1000,3000,没收到2000,应答时不会应答3001,只会应答1001【后面还会细讲此场景】
tcp协议段中有了32位序号后为什么还要有32位确认序号
- 接收方应答时可能同时发送数据那么就需要指明自己发送数据的序号。
- 发送方在发送数据的同时可能也在接收数据!双方地位对等。
思考三个问题【demo】
- 之前模拟浏览器服务端 我们只在应用层编码 至于数据怎么传的 底层实际上做了很多工作 在应用层我们不学 之后我们慢慢学
- 16位窗口大小:发送方自己的接收缓冲区剩余空间大小。但是历史上第一次发送方向接收方发信息,他怎么知道接收方的窗口大小?即他怎么知道他要发多少数据才合适?
- 多批量传输时 报文和应答丢失的情况。
标记位
6个标记位
建立链接三次握手 开始进行数据通信 断开连接四次挥手 。在tcp通信时,这些行为都会发生tcp报文,不同的行为引发不同的动作,意味着tcp收到的报文是有各种”类型“的,不同的类型决定了接收方要做不同的动作。接收方如何得知报头的类型?通过6个标记位!标记位的意义:区分tcp报文的类型!
简述
您已经列举了TCP协议中的六个重要标志位(flags),每个标志位都在TCP通信中扮演着不同的角色。以下是这些标志位的简要介绍:
URG (Urgent):
作用:表示TCP报文段的紧急指针(Urgent Pointer)是否有效。
解释:当URG标志位被设置时,TCP的紧急指针会指出紧急数据在报文段中的位置。这允许发送方标记一些数据为“紧急”,这样接收方就可以优先处理这些数据,而不是按照正常的顺序来处理。
ACK (Acknowledgment):
作用:表示确认号(Acknowledgment Number)是否有效。
解释:ACK标志位用于确认接收到的数据。当TCP发送一个报文段时,它会期望对方返回一个包含ACK标志的报文段,以确认数据的接收。ACK确认号表示接收方下一个期望接收的字节的序列号。
PSH (Push):
作用:提示接收端的应用程序立刻从TCP缓冲区把数据读走。
解释:PSH标志位用于通知接收端的应用程序尽快从TCP的接收缓冲区中读取数据。在某些情况下,即使接收缓冲区中的数据量未达到应用程序的读取阈值,PSH标志也可以确保数据被立即读取。
RST (Reset):
作用:对方要求重新建立连接;我们把携带RST标识的称为复位报文段。
解释:RST标志位用于异常关闭连接。当TCP检测到严重错误(如主机崩溃)或者收到一个非法的报文段时,它可能会发送一个RST报文段来关闭连接。RST报文段不携带数据,并且会释放所有关联的资源。
SYN (Synchronize):
作用:请求建立连接;我们把携带SYN标识的称为同步报文段。
解释:SYN标志位用于建立一个新的连接。在三次握手过程中,第一个报文段(SYN报文段)会包含SYN标志位,并设置初始的序列号。接收方会回复一个SYN-ACK报文段(包含SYN和ACK标志位),表示它同意建立连接,并确认收到的序列号。
FIN (Finish):
作用:通知对方,本端要关闭了;我们称携带FIN标识的为结束报文段。
解释:FIN标志位用于正常关闭连接。当一方决定关闭连接时,它会发送一个FIN报文段给另一方。接收方会回复一个ACK报文段来确认这个FIN报文段,并在完成所有数据的发送后,发送一个自己的FIN报文段来关闭连接。
这些标志位共同构成了TCP协议的核心部分,确保了数据在网络中的可靠传输。
总结
ACK/SYN/FIN:以非数据传送为目的 有一些控制传输的含义在其中。OS不会直接暴露这些标记位给用户 但是或多或少会给用户提供接口让用户设置 如connect–》syn;close–》fin:给对方发一个含有fin标记位的报文
详述
URG: 紧急指针是否有效==提升某报文被处理优先级【0表示不设置1表示设置】
一般情况,tcp数据报文(一个大数据分成很多小数据发)按123456顺序发,接收时不一定是123456,但是收到后他要根据32位序号排序成123456。在tcp协议下,某些报文是不允许插队的,即接收方只能按照特定顺序依次处理报文。但是如果接收方收到了带有urg的报文,TCP协议段的16位紧急指针会指出紧急数据在报文段中的位置,接收方收到后会优先处理该数据包。
紧急指针:报文中有效载荷中的紧急数据的偏移量。
tcp中一个报文中一般只允许出现一个字节的紧急数据。
如何使用urg?
在支持带外数据的套接字上发送带外数据(例如SOCKSTREAM类型);底层协议也必须支持带外数据。
此标志请求接收正常数据流中不会接收的带外数据。一些协议将加急数据放在正常数据队列的头部,因此这个标志不能用于这样的协议。
什么场景用?
c向s发起服务请求,s响应很慢或者几乎不响应,但是s并没坏,他可能因为某种计算很复杂而一直计算,此时c并不知道持续向s发送请求,但是没有响应,于是c向s发送了带外数据(紧急数据)询问s你到底在干嘛?s中有例程专门处理紧急数据,相应一个带有服务器状态的报文给c让c知道s在干嘛。(s要支持读取紧急数据,且s在软件设计上设计了某一状态用某一种编号代替)
ACK: 确认号是否有效
三次握手成功后 几乎所有的应答报文的ack都是置为1的,表示我是一个应答报文,至于是不是捎带应答,看看是否存在有效载荷。
PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
情景:
数据从网络里发到接收方 OS把网络数据存至接收缓冲区 用户层决定读不读接收缓冲区里的内容。如果用户一直不读 接收缓冲区里的空间就会越来越少。OS把网络数据放到接收缓冲区,用户从接收缓冲区读==》PCMode!流量控制–》发送过程【发送者的发送缓冲区到接收者的接收缓冲区】的同步!接收方接收缓冲区的数据一直不被取走,发送方发送缓冲区的数据有一天会被拷贝满,这时在发送方,用户层就不能再向发送缓冲区写数据—》写阻塞!发送方阻塞等待!等到什么时候?有没有判定依据?1. 定期询问对方2. 当接收方接收缓冲区有空间了,给发送方发通知。 接收方一直不读呢?发送方就会发送一个带有psh标记位的报文!接收方就会得知发送方已经着急了!提示接收端应用程序立刻从TCP缓冲区把数据读走!!同样,一个合格的服务器也应该及时的读取数据!宏观上提升网络通信效率!也有可能,一个捎带psh即带有数据+psh标记位的报文!让服务器尽快接收数据。
RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
tcp协议保证可靠性:正常通信下 tcp提供对于数据的很多种可靠策略 但是也有一些情况无法避免:客户端不想通信了 也不挥手 直接拔了网线。所以tcp的可靠性的前提是不会出现一些不可抗拒的异常问题! 即tcp不能保证三次握手每次都成功!即便三次握手不成功但是三次握手每一步都做了 tcp也会开始通信。tcp允许链接建立失败!服务器内部可能存在多个建立好的链接(他在跟多个客户端通信)。服务端存在很多链接,管理–》先描述再组织!链接—结构体;属性—成员变量;很多链接—链表;
维护链接有成本
创建内核数据结构对象 初始化 填充字段==》空间时间
链接太多 管理不过来 异常!
客户端认为链接建立好了 服务端认为没建立好 服务端发送带有rst标记位的报文要求重新建立连接;
画图线是斜的:数据经过网络—有延迟!
问题:首先要知道,三次握手重在三次,至于是否建立好了连接不知道。
服务端接收回应接收三次后认为三次握手已完成。而客户端最后一次发送并没有响应他会认为只要他第三次发出去了就握手了–就建立链接了!不考虑重传,正常情况下,客户端不知道服务端是否收到!tcp在赌第三次的ack能被服务端收到!既然第三次ack一经发出,客户端就认为建立好了连接,那么客户端此时就会创建连接的结构体对象并初始化填充属性准备通信。但是ack经过网络发到服务端需要时间,在这个时间间隔内,CS对于是否建立好连接认知不一致!此时客户端认为建立好连接了,就会开始给服务端发送数据。如果服务端没收到这个ack却突然收到了一个数据,服务端就会意识到最后一个ack丢失了,对方虽然建立好连接了但是我没有,于是服务端就给客户端发送一个带有rst标记位的报文!
rst标记位只有这一种场景吗?
三次握手双方都建立成功了即第三次ack成功发送,但是服务端断网重启了,客户端不知道,发送信息时服务端就会回应一个带有rst的报文。即rst应用于建立连接时正常通信中,都会出现协议/网络问题导致要使用rst
第三种场景
服务器满载了,即建立连接数已最大,客户端就会给服务端发送带有rst的报文请求重新建立连接。
SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
总结标记位
tcp报文:有的用来进行数据通信 有的用来进行特殊功能–连接管理等 或者二者皆有之。
总结报头属性
tcp是可靠的协议 故协议中有很多属性就提高了可靠性,有的属性为了提升传输效率。有很多为可靠性的设计的策略在tcp中也是体现不出来的比如流量控制,重传。下一篇讲。