赞
踩
目录
与UDP报文结构相似,TCP报文中 端口号 也占2个字节,包括 源端口号 和 目的端口号。
端口号 就是用来区分一台主机上不同的应用程序。
源端口号信息的意义在于,发送的数据 来自 发送方主机的 哪个程序。
目的端口号信息的意义在于,要把数据发送到 接收方主机的 哪个程序。
首部长度 规定的是 TCP数据报 报头的长度,单位为4字节。选项也是报头的一部分。TCP报头,最短是20个字节,最长是60个字节。
当TCP报文的报头长度超过20字节时,超过的部分长度就来源于选项。因此TCP报头最短20字节(不包含选项),最长60字节(选项最多为40字节),选项都是以4字节为单位。
保留位是 预留的6位长度,以备不时之需。
与UDP协议报文中的检验和是一致的。
TCP报文中有序号和确认序号,这与TCP的机制相关,下面继续说明。
传输层有许多协议,TCP和UDP是传输层中两大重要协议。
在探讨TCP协议之前,首先要了解 可靠传输 这一概念。
可靠传输,也就是 发送方 将数据 发送给 接收方之后,接收方收到数据 会给发送方发送一个应答报文,这样发送方就知道了自己发送的数据对方收到了。如果发送方没有收到接收方的应答报文,则发送方就会知道刚刚发送的数据出了问题,就会采取相应的措施,比如重传。这么一个通信过程,就称之为 可靠传输。
TCP协议相比于UDP协议来说,TCP最核心的特点就是 可靠传输。而TCP的可靠传输,是要靠TCP的许多机制来支持的,那么TCP有哪些机制可以帮助实现 可靠传输 这一特性呢?
确认应答,是TCP的最核心机制,支持了TCP的可靠传输。
确认应答:发送方把数据发送给接收方之后,接收方会反馈给发送方一个应答报文(ACK),发送方收到了这个应答报文,就知道刚刚发送的数据对方收到了。
就比如A给B发送了一条消息“在吗”,B回复了“在”,这样A就知道B收到了自己的信息,就可以继续和B聊天了。
此时有一个问题,在网络传输数据的过程中,往往会发生 发送的数据出现"先发后至"的情况。那么这时,A给B发送了多个数据包,B能否 根据 发送方发送数据的先后顺序 进行 有序应答呢?
答案是肯定的,即使A发送的数据报,在网络传输的过程中出现了“先发后至”的情况,B也能够根据 发送方的发送顺序 来进行 有序应答。那么依靠什么来实现有序应答的呢?
就是 根据 TCP报文中的 序列号 和 确认序列号 来完成有序应答的。
A给B发送了1-100序号的数据,B收到后,告诉A自己收到了(ACK),可以接收A第201个序号的数据了。通过序列号,接收方B就可以针对A发送的每个数据进行对应的应答了。
TCP协议初心就是为了实现 可靠传输,而TCP实现可靠传输的最核心机制就是 确认应答。
那么,通信双方如何区别每个数据包是普通数据,还是应答报文呢?
答案就在TCP报文中的ACK标志位
如果ACK标志位为1,则这个数据就是一个应答报文,"确认序号字段"生效。
如果ACK标志位是0,则这个数据就是一个普通报文,"确认序号字段"就不生效。
当然,要实现TCP的可靠传输,并非只依靠 确认应答 就可以了,还需要TCP其它机制进行辅助。
超时重传,顾名思义就是 超过一定的时间就进行重传。什么时候会触发超时重传呢?
数据包在网络进行传输的过程中,常常会出现"丢包"现象。
此时,"丢包"存在两种情况:
A给B发送了一个数据包,如果数据包在传输的过程中出现了"丢包"情况,那么A就无法收到B的应答报文了。站在发送方A的角度,A也无法分辨出到底是 数据包没传输到B,还是B的应答报文丢了。无论是上述的哪种情况,TCP都会触发重传机制,即A重新将该数据包发送给B。重传机制,大幅度提升了 数据能被成功传输给B的 概率。
至于什么时候再进行重传,等待时间是可配置的。
【总结】:当A发送数据给B,等待一定时间之后,若A还没收到B发来的应答报文(数据包发生"丢包"情况),此时就会触发超时重传,A就会重新给B发送该数据。如果多次重传,仍然没有收到B的应答报文,A就会直接放弃与B连接了。
对于以下这种情况,A成功将数据包发送给B了,但B发送的确认应答报文"丢包"了,于是A又重传了,那么此时B就会收到两份一样的数据包。那么B会怎么办呢?
TCP有一个"接收缓冲区",也就是一个 内存空间,会保存发送方 发送过来的数据,以及数据的序号。如果接收方发现当前读取的数据,和"接收缓冲区"中的数据有重复,就会把当前数据包丢弃。也就是说TCP的"接收缓冲区"有"去重"功能。TCP的"接收缓冲区"不仅有去重功能,还有 "整队"功能,也就是当A 发送的多个数据包 成功到达 B方时,接收方的"接收缓冲区"会 按其发送顺序 整理好 数据包的到达顺序。这一功能可以确保 发送数据的顺序 和 读取数据的顺序 是对应的,就不会出现"牛头不对马嘴"的情况了。
TCP协议的特点是:有连接,可靠传输,面向字节流,全双工。
因此,使用TCP协议来进行网络通信时,通信双方会首先与对方建立好连接,然后再继续通信。
这里的 "建立连接" 也就是 通信双方 保存 对方相关信息 的一个过程。
连接管理 包括 "三次握手" 和 "四次挥手"。
"三次握手" 是 通信双方 建立连接 的过程;"四次挥手" 是 通信双方 释放连接 的过程。
"三次握手"这么一个建立连接的过程,也就是通信双方在正式通信之前的一个打招呼过程。一般主动发起 建立连接的一方是 "客户端",被动接受的一方是"服务器",也就是接收方。在这个过程中,通信双方都会给对方发送一个 简单的,没有业务数据的 数据包。
比如,A要和B通信,在正式通信之前,A和B就会有一个"三次握手"的过程来建立连接,A会问"你在吗?",B会回复"我在,你还好吗?",A也会回复"我很好!"。这么一个过程就代表通信双方建立好了连接,可以正式通信(聊天)了。
三次握手的过程如下图:
这就完成了握手,双方也就建立好了连接。双方在三次握手的过程中,双方都会给对方发起SYN同步报文段(没有载荷的数据包),也都会给对方回复一个ACK。
其中接收方B的SYN和ACK可以合并成一个数据进行发送,因为SYN和ACK都是由内核触发的,同一个时机触发,可以合并发送。
"三次握手"是为了与对方建立连接,这个过程有什么意义呢?
TCP的核心特性是 可靠传输,确认应答 和 超时重传 是 实现可靠传输 的一个大前提,若通信双方的网络环境有故障,又如何能实现可靠传输呢?"三次握手"这么一个过程的意义就在于此:
(1)确认当前网络是否畅通
(2)确认 通信双方的 发送和接收能力 是否正常。
(比如A给B发消息,A问道“你在哪?”,B回答"我在家"(保证A的发送能力 和 B的接收能力是正常的),A回复“好的”(保证A的接收能力 和 B的发送能力是正常的)。)
(3)通信双方在"三次握手"的过程中,针对一些重要参数进行"协商"。
(要"协商"的信息之一就是 这次发送方要发送的数据序号是从几开始,一般都会与上次通信的数据序号有很大差异,避免接受方收到的是以前的旧数据。)
通信双方如何区分这是 同步报文段还是 普通的数据包,也是通过SYN标志位来区分的。
若SYN标志位为1,则"同步报文段"标志位生效。
若SYN标志位为0,则"同步报文段"标志位不生效。
"四次挥手" 就是 通信双方 释放连接 的一个过程。通信双方都可以主动发起释放连接。
四次挥手,为什么是四次而不是三次,主要是接收方发送的ACK与FIN一般不能合并发送。因为这里ACK和FIN触发的时机不一样。ACK是内核响应,B一旦接收到A发来的FIN,就会立刻回复ACK。而只有等到B这边代码调用了socket的close方法,才会触发FIN(应用程序的代码触发)。至于等B发出ACK多久后,B才会调用close方法就无法确定了。因此释放连接是"四次挥手"。
"四次挥手"这个过程,通信双方都会给对方发送FIN结束报文段 和 ACK应答报文,与"三次挥手"不同的地方就在于,B收到FIN后,会立刻回复ACK,等待应用程序调用close方法之后,再回复FIN,最后A再回复一个ACK,"四次挥手"的过程就完成了,A与B就彻底断开了连接。
哪一方主动断开连接,哪一方就会进入"TIME_WAIT"状态,这个状态的意义是 防止最后一个ACK丢失。
如果没有"TIME_WAIT"状态,且最后一个ACK丢失的话,站在B的角度,等待一定时间之后,仍没收到A发来的ACK,B就会以为自己发出的FIN报文丢失了,于是B会重传FIN。但A发送了ACK之后就释放连接了,无法再处理FIN报文了。那么B重传的FIN就没人处理了,则B重传FIN也就没有意义了。
A有了TIME_WAIT这个状态,发出最后一个ACK后,会等待一定的时间,确保ACK能成功发送给B。即使此时ACK丢失了,B也会重传FIN,A也能及时处理FIN,发送ACK给B。
至于TIME_WAIT会等多久,如果网络上两个节点通信消耗的最大时间是MSL,则TIME_WAIT的时间就是2MSL。
确认应答,超时重传,连接管理,这三个机制都是在 帮助实现 TCP的 “可靠传输” 。也正是由于TCP的可靠传输这一特性,使得TCP的协议更加复杂,数据传输效率也会比较低。因此 滑动窗口 就起到了一个"亡羊补牢"的作用,尽量 提高TCP的数据传输效率。
如果没有"滑动窗口",TCP协议传输数据时,发送方只有等到应答报文后,再传输下一个数据。这个过程会有许多的等待时间,使得传输效率非常低。
有了"滑动窗口"这一机制,TCP就可以"批量传输"数据了。当然批量传输也存在一定上限,达到上限后,会统一等待ACK。在不等待ACK的情况下,批量最多发送的数据量 就称为 “窗口的大小”。
如下图,此时 A -> B 批量发送了5份数据,B也要给A回复5个ACK应答报文。当前 A已经达到了窗口大小,在收到ACK之前,不能往下发送数据了。因此,A在等待B回复第一个ACK。
现在的问题是,A是等收到5个ACK之后,再往下发送数据,还是收到一个ACK就可以往下发送一份数据呢?
答案是 不用等待所有的ACK到达,只要有一个ACK到达之后,A就可继续往下发送数据了。因此,滑动窗口越大,TCP的数据传输效率就越高,当然窗口大小也是有一定上限的。 ·
如果在数据传输的过程中,出现了"丢包"情况,会如何处理呢?
既然数据"丢包"了,那么就会有重传,此时的重传 与 上面的 超时重传 有一定的区别。
如果是 ACK丢失了:
此时不需要重传。如果1001,2001,3001的ACK丢失了,但是4001的ACK收到了,就说明4001之前的数据也都传输成功了
如果是 数据包 丢了:
此时A会发现B这边连续几个ACK都在索要2001的数据,A就知道2001数据丢失了,就会重传2001,当2001-3000数据成功到达B这边后,B就会索要6001的数据了。
这个重传的过程中,没有什么额外的操作,只要A发现B重复几次索要同一段数据,A就会知道该段数据丢失了,就会重传该段数据,这个重传的过程比较迅速,可以说是 快速重传。没有丢的数据就不用重传,A可以继续往下发送数据了。
如果通信双方的数据传输量不大,也不频繁的话,TCP就还是以 普通的确认应答 和 超时重传 的模式来传输数据。
如果通信双方的数据传输量很大且频繁的话,就会进入滑动窗口模式,以 快速重传 的方式来处理数据。
虽然滑动窗口越大,发送数据就越快,数据传输效率越高。但当滑动窗口达到一定大小后,发送方发送的数据太快,接收方处理不过来了,这就会出现"丢包"的情况,数据发生丢包后,发送方还得去重传,这就得不偿失了。因此 TCP并不会让滑动窗口无限增大,这就有TCP的流量控制来处理了。 因为TCP的初心是为了实现"可靠传输",希望在此基础上 再尽可能 提高传输效率。
流量控制这一机制,则是站在接收方的角度,制约发送方的数据传输速率。也就是为了保证 发送方的发送速率 不超过 接收方的接收能力。
A发送的数据包 成功发送给B后,数据会先放在 接收方的"接收缓冲区"中,B就会从“接收缓冲区”中读取数据,B读过的数据,该数据就可以从接收缓冲区中删除了。
接收方 接收数据的能力 就 表现在 接收缓冲区中的 数据量。
接收缓冲区中数据越多,则接收方的数据处理能力就越低。也就是接收缓冲区的剩余空间越小,则说明接收方的数据处理能力越弱。反之,接收缓冲区的剩余空间越大,说明接收方处理数据能力越强。
接收方每次收到数据后,都会通过 ACK 把 接收缓冲区的剩余空间大小 返回给发送方,发送方就会按照 剩余空间这个数值 来调整下一轮的数据发送速率。
如果接收方反馈给发送方的 接收缓冲区剩余空间为0,则发送方就会暂停发送数据了。那么发送方会暂停多久才会再开始发送数据呢?
发送方暂停发送数据后,等待一定的时间,发送方会周期性的(防止"窗口探测包"丢包)给接收方发送一个"窗口探测包",不携带业务数据。"窗口探测包"是为了触发接收方发送ACK(这个数据包中就会有"接收缓冲区"剩余空间的数值),接收方的接收缓冲区不满时,接收方回复给发送方的ACK就会告诉发送方可以 继续发送数据了。
接收缓冲区的 剩余空间的数值保存在TCP数据包中:
流量控制 这一机制是 制约发送方 的 发送数据的速率,而 "拥塞控制"则是考虑整个 通信路径 的情况。接收方的接收数据能力 可以 通过 "接收缓冲区" 来 衡量,而整个通信路径 结构更为复杂,导致 通信路径的状况 难以有 "具象化"的衡量标准。因此,只能通过 "实验"的方式,来确定当前的一个数据传输量。
拥塞控制 这一机制,就是通过"实验"的方式,来逐渐调整 发送方的数据传输速率:
最开始,TCP会让 发送方 以一个比较小的窗口来发送数据,等窗口大小达到一定程度后,可能中间的传输节点就会发生问题,就可能会出现"丢包"问题。发送方发现 数据"丢包"后,就会缩小发送窗口大小,如果仍在"丢包",就会 继续缩小。如果不"丢包"了,就会继续 尝试 增大窗口。在这个"实验"的过程中,发送方在不断调整窗口大小,从而达成一种 "动态平衡"的状态。
此图就是拥塞控制的调整窗口大小的过程。当窗口到达一定程度时,会降低窗口的增大幅度;当出现"丢包"时,会大幅度缩小窗口,设置新的窗口门限值,再慢慢增加窗口大小,从达到一种"动态平衡"的状态。 因此,"拥塞控制"的意义就在于 保证数据在传输的过程中尽可能避免"丢包"问题,且尽可能的提高传输效率。
一般情况下,发送方A 发送数据包 给 接收方B 后,B会立刻返回一个 ACK。但有的时候,B不会立刻回复ACK,而是 等待一会儿 再返回ACK给A,本质上也是为了提升数据传输效率。
延时应答,会延时返回ACK,给 接收方 更多的读取 "接收缓冲区"中数据的 时间,有了这一段延时,接收方可以有更多读取数据 的时间,也就可以让"接收缓冲区"中的剩余空间更大(发送方的发送"窗口"大小 会根据 接收方"接收缓冲区"剩余空间大小 来 调整),也就可以让发送方有更大的发送"窗口",也就使得发送方可以调整到更大的数据传输速率。这就是为什么说延时应答本质上是提升数据的传输效率。
在日常上网的过程中,我们往往会访问 某个页面,这是一个 向服务器发出 访问请求的过程;服务器收到请求,也就会 作出 响应,我们才能成功看到想访问的页面。
请求 和 响应 的过程如下:
"捎带应答"机制 则是在 延时应答 的基础上 进一步提高 传输效率。
在请求与响应的过程中,当服务器收到 请求后,会立刻回复 给发送方 一个ACK,当服务器计算好响应后,再返回RESPONSE。
由于TCP的"延时应答"机制,ACK有时候可能不会立刻回复,而是等待一定的时间再回复ACK,也正是由于等待的这一段时间,服务器有可能也计算好了响应。因此,在这种情况下 服务器可以把 ACK与RESPONSE 一起返回给 客户端,提高了一定的传输效率。
TCP协议的特性之一是 面向字节流,也就是TCP传输的数据是以"字节"为单位。
那么通信双方进行交互的过程中 存在一个问题:当发送方 发送了多个数据包 放在"接收缓冲区"时,接收方会去读取 "接收缓冲区"中的数据,这个读取数据的过程 就可能会发生"粘包"的问题。
什么是"粘包"问题:
"接收缓冲区"中存放的是 发送方 发送的多个数据包,这些数据都是以字节为单位紧紧放在一起的。接收方的应用程序每次读取数据时,可以一次读取一个或多个字节的数据,目标是为了获得完整的数据包。但接收方的应用程序难以辨认清楚 数据从哪里到哪里是一个完整的数据包。
如下图,发送方发送了三个数据包,分别是"you" "is" "book",接收方的应用程序读取数据的时候,就不知道 从哪里到哪里是一个完整的数据包
因此,在接收方读取数据的过程中,要避免"粘包"问题。
那么如何避免"粘包"问题:通过定义好应用层协议,明确应用层数据包之间的边界。
以下是两种引入边界的方式:
(1)引入分隔符:在每个数据包(末尾添加一个结束符,当应用程序读到该符号时,就知道一个完整的数据包已经读取完毕了。
(2)引入长度:在每个数据包的数据开头,标明此数据包的数据长度,接收方读取数据时,都会根据该长度读取相应的字节数,进而就能读到一个完整的数据包了。
在数据传输的过程中,难免会出现各种意外情况,比如 进程崩溃,主机关机,主机断电,网络断开等。使用TCP协议传输数据时,遇到这些情况,TCP也会有相应的处理方式。
(1)进程崩溃
进程异常崩溃了,也就意味着进程结束了,则该进程的文件描述符表也就释放了,相当于调用了socket的close方法。此时就会触发正常的"四次挥手"的过程。
(2)主机关机
在关机的时候,也会触发 进程强制终止 的操作,也是正常的"四次挥手"过程,从而释放连接。但是,当进程销毁了,也就意味着主机也关机了。若在进行"四次挥手"的过程中,接收方发来的ACK和FIN迟到了,主机已经关机的话,就无法处理ACK和FIN了,也就是不会给对方回复一个ACK了。此时,站在对方的角度,没等到ACK,就会超时重传 FIN,若重传几次仍没有响应,自然就会放弃与对方的连接了(删除 之前保存对方的相关信息)。
(3)主机断电
主机断电,一般是意外情况,并非主动断电。那也就意味着 主机还没来得及 关闭进程,主机就关机了。那也因此,主机没有时间与对方进行"四次挥手"。
此时,有两种情况,发送方断电 和 接收方断电。
发送方断电:这种情况,接收方会在等待对方传数据过来,等了一定时间,仍没消息,无法确定发送方是什么情况。于是 接收方 会 周期性的 给 发送方 发送 不带业务数据的 数据包(TCP的"心跳包"机制),期望收到对方的应答。如果发送了多个该数据包,仍没有得到回应,接收方就会单方面释放连接了。
接收方断电:发送方 发送数据后,会等待对方回复ACK,等待一定时间后,仍没等待ACK,同样是触发超时重传,且触发TCP连接重置功能,发起"复位报文段"。如果复位报文段发过去之后,仍没有效果,也就会释放连接了。
(4)网络断开
与 主机断电 类似,若是发送方断网了,接收方会发送"心跳包",无回应,就 释放连接。若是接收方断网,发送方触发超时重传,且发起"复位报文段",无回应,就 释放连接。
TCP与UDP协议是传输层中的两个非常重要的协议 ,这两个协议有一些共同点和不同点:
相同点:都支持 全双工通信(通信双方都能给对方发送信息)
不同点:
(1)UDP面向数据报,TCP面向字节流。
也就是说UDP协议处理数据的基本单位是 数据报,TCP协议处理数据的基本单位是 字节。
(2)TCP是有连接的,UDP是无连接的。
意思是 使用TCP协议完成通信时,通信双方会保存对方的相关信息,建立好连接,然后再通信。而使用UDP协议通信双方不会保存对方的信息,不用建立连接,直接通信。
(3)TCP支持可靠传输,UDP是不可靠传输。
可靠传输,也就意味着 双方在通信时,发送方能够知道 自己发送的信息 对方是否收到了,如果对方没收到,自己也会采取相应措施(比如 重传)。
TCP的 确认应答 是 实现"可靠传输"的核心机制,超时重传 和 连接管理 是进一步保障"可靠传输"。因为TCP的机制相比于UDP要 复杂许多,也就意味着 传输效率 无法与UDP 相比,于是 TCP的滑动窗口、流量控制、拥塞控制、延时应答、捎带应答等机制 则是在尽量提高TCP协议的传输效率,尽量弥补 与UDP的传输效率的 差距。
UDP与TCP协议各有各的适用场景,各有各的优势。UDP的优势在于传输效率高,TCP的优势在于 "可靠传输"。
以上则是TCP协议的相关重要知识。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。