之前我们有说到过,我们程序猿主要编写的代码其实就是应用层的,但是传输层作为直接接触到应用层的层次,我们或多或少也是需要有一定的了解的.
TCP协议
)
TCP,即Transmission Control Protocol,传输控制协议.
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/ef7d8665dcd62a4cdb87bd78cf28dd27.png)
源/目的端口
参考源/目的IP,IP表示网络中主机的位置,那么源/目的端口也就是网络中的主机上具体到某个进程的位置.
32位序号/确认序号
序号和确认序号这个其实都是后话了,我们在发送信息的时候是存在后发先至的现象的,此时我们就需要给这些个序号排排号,发送方会发出信息的序号,接收方如果接收到了就会回一个确认序号.
4位TCP报头长度
表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是15 * 4 = 60(4位最多表示到15)
6位标志位:
URG:紧急指针是否有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
比特就业课
SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
16位窗口大小
窗口大小就决定了我们接受数据的速度
16位校验和
校验和相当是是一种保底措施,校验和对了,数据不一定对,但是校验和都是错的,数据就不可能对了.
16位紧急指针
标识哪部分数据是紧急数据;
40字节头部选项
可能包括"窗口扩大因子"、"时间戳"等选项
TCP相比于UDP是可靠传输,那么接下来我们先来说一说可靠性:
安全性
确认应答
TCP将每个字节都标上了序号.这就是序列号.
每一次数据的接收方要给数据的发送方返回一个ACK的时候都会把已经接收到的数据的序号给返回,这就是在告诉数据的发送方,我已经拿到了这些个数据,你直接发后面的数据就好了.这样子就避免出现明明链接已经断开,你却还在傻傻等待的尴尬了.
超时重传
如果数据的发送方长时间没有接收到带有确认序号的ack,那么我们就会往最坏的方向上思考,是不是我们两个谁不行,然后重新发送一次数据,当然了,也许网络比较繁忙,所以超时重传是在等待了一定的时间的前提下,才会发生的机制.
连接管理机制
三次握手
在正式上班之前,我们都会有个实习期,同样的在正式传输数据之前,我们也是需要先尝试建立连接,看看有没有什么壁垒.
就像打电话来说,A给B打
A:喂喂喂,听得到吗(尝试发出一个信号)
B:嗯呢(如果B能听到就会发出这样的信号,表示对方的话筒和自己的扬声器没有问题)
A:okok(表示对方的话筒还有自己的扬声器没有问题)
大致上就是这么一个过程.
当然了其中如果有丢包,还是会触发超时重传机制的.
结合接电话的过程来理解我们的三次握手应该会容易不少.
本来按照道理来说,总共应该是会有四次请求响应的发送的,但是在服务器发送的响应的时候夹杂了私货,也发送了一个小小的试探,“我能听到你的声音,那你能听到我的声音不”,就是这么一个情况.
四次挥手
书上说了,天下没有不散的宴席,三次握手建立连接,四次挥手也就是为了分手做了铺垫.
三次握手的发起方一般来说都是客户端,因为我们之前说了主动发起请求的一方就是客户端,但是四次握手的发起者就不一定了,文明社会婚恋自由,我随时随地可以甩了你.
四次挥手与三次握手最大的区别就是,多了一次连接的发送,这时候兄弟们是不是就感觉很奇怪了,为什么在三次握手里面我们就可以把SYN和ACK放在一起,但是四次挥手就不行了呢?
那是因为这里ACK和FIN的时机是不一样的,ACK是用户层,FIN则是内核实现的,虽然我们的起点和终点一样,但是我们的交通方式不一样,我们的行程自然也不一样了.
流量控制
流量控制机制就是TCP会根据接收方接受的能力来决定发送方的发送速度.
每一次接收方返回的ACK中也会偷摸带上一个窗口大小的字段,发送方会根据这个字段来动态调整自己的发送速度,一旦这个字段为零了,发送方就不再发送数据了,而是发送一个试探性质的SYN,看看接收方可不可以返回一个不是零的窗口大小.TCP首部里面包含了一个十六位窗口字段,存放窗口大小信息,还有一个窗口扩大因子M
拥塞机制
实际上我们数据传输不仅仅就是一个接收方一个发送方这样简单的模型,在数据的传输过程中必然会经过许许多多的网络设备,这其中只要有一台设备发生了重大故障对于我们的数据传输来说都是不小的影响,这点参考木桶效应.所以为了避免刚开始上来就是高强度的数据传输,我们要循序渐进,刚开始会以一个很小的速度开始,由于此时的输送压力较小,这个速度会以指数形式增长,当速度增长到一定程度的时候,此时丢包的概率大大提升,转化成线性增长,当发生了第一次丢包的时候,再次回到开始的速度,同时阙值变成第一次丢包时的速度的一般(阙值就是由指数增长变成线性增长的值).
效率性
滑动窗口
刚才我们讨论了确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。给人的感觉就好像实际上做事情的时间不是很多,大部分的时间都用在了等待ACK.
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000
个字节(四个段)。发送前四个段的时候,不需要等待任何ACK,直接发送;收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉;
窗口越大,则网络的吞吐率就越高.
在这种形势下,如果我们的ACK丢包了其实也就显得不是那么重要了,因为即使我们1-1000的ACK没了,但是后面我们还会发送1000-2000,2000-3000的ACK,我们之前说过,发送1-1000的意思有两层,第一就是1000之前的数据我都接受到了,第二就是催促赶紧发送2000-3000的数据.
如果是报文段丢失了,那么接收方会一直返回一个1-1000的ACK,告知发送方我还缺这一段,一旦拿到了这一段,接下来发送的可能就不是2000-3000而是7000-8000这样的ACK了.
延迟应答
延迟应答就是一个拖字诀,上学的时候作业写得最快的时候永远是要交作业的前几个小时,你每晚一会让我交,我都能多写一点.
延迟应答也就是这么一个意思,我慢点给你发送ACK,那么下一次我发送的时候就能多传回一点.
捎带应答
之前我们不是说过四次挥手里面的那一次ACK和SYN不能一起发送是因为时机不对嘛,那么现在我们稍微改变一下时机,让走的快的等等走的慢的,让走的慢的走的快点,是不是就有可能让他们一起发送出去呢?要知道一次发送两个比发送两次一个要来的快哦~
面向字节流
TCP是以字节流为单位的,每次发送绝不可能出现两个半字节流这种情况,当我们有二百五十个字节的内容要发送的时候,我们可以选择一次性发送二百五十个字节,亦或是发送二百五十次一次发送一个.
但是我们之前说过分散成多次分发不是高效的选择,因此我们就有了缓存区这个机制,每次我们想要发送数据,不是直接发送,而是存在缓存区里面,当存到了一定地步的时候一股脑地发送出去,同理,当我们接受数据的时候也是等到接收缓存区里面有了一定量的数据才会真正接收.
粘包问题
这里的包指的是应用层的数据包,在TCP协议头里面是没有UDP一样的报头长度的,有的只是一串串的序号,TCP会按照序号把这些数据排好队伍,但是站在应用层的眼光上面,这些就是一连串的字节数据,是分不清从哪到哪是一个数据的,为了解决粘包问题,我们要做的就是明确报与包之间的界限,如果是确定长度的数据包,我们每次只需要读取固定长度的字节就行了,但是对于不确定长度的数据包我们选择的方式是指定分隔符,读到分隔符我们就知道了这是一个数据.