文章目录
- TCP协议
- TCP的结构
- TCP的特点
- TCP如何保证可靠传输
- 确认应答(可靠机制)
- 超时重传(可靠机制)
- 连接管理(可靠机制)
- 滑动窗口(效率机制)
- 流量控制(可靠机制)
- 拥塞控制(可靠机制)
- 延时应答(效率机制)
- 捎带应答(效率机制)
- 面向字节流
- 异常处理
- 进程崩溃
- 主机关机(正常情况)
- 主机掉电(非正常情况)
- 网线断开
TCP协议
TCP的结构
源端口号和目的端口号就是说数据从哪里来到哪里去。
4位首部长度:TCP报头的长度是不固定的报头最短是20字节(没有选项),报头最长是60字节(选项最多是40字节)。
校验和:和UDP协议一样。
保留位:因为UDP的长度是64kb,我们无法改变,所以发明TCP的人做出了改变,加入了保留位,先占个位置,如果后面需要就可以进行扩展使用。
TCP的特点
1.有连接:发送方和接收方会保留对方的信息(如果A和B建立连接,B拒绝了,就无法建立连接,通信就无法完成)
2.可靠传输:A给B发消息,消息有没有成功送达A可以感知到(如果发送失败就可以进行采取一定的措施)
3.面向字节流:TCP的传输和文件操作一样都是以字节为单位的
4.全双工:一个通道允许双向通信就是全双工反之就是半双工(一个socket对象既可以发送数据也能接收数据)
TCP的核心就是可靠传输
TCP如何保证可靠传输
确认应答(可靠机制)
发送方把数据发送给接收方后,接收方收到数据之后就会给发送方返回一个应答报文(acknowledge 简称ack),发送方收到应答报文后就知道自己的数据发送成功了。
每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据,下一次你从哪里开始发。
如何确认一个数据包是普通数据还是ack数据呢?
上面的TCP结构图中ACK为1,就表示当前数据包是一个应答报文,此时该数据包中的确认序号字段才能生效,如果这一位为0,则表示当前数据包是一个普通报文,此时该数据包中的确认序号字段不会生效。
通过特殊的ack数据包里携带的确认序号告诉发送方哪些数据已经被收到了,此时发送方就知道自己刚刚发送的数据是否成功。TCP的初心就是可靠传输,达成可靠传输的核心机制是确认应答。
超时重传(可靠机制)
A发送数据给B后,可能因为网络拥堵等原因,数据无法到达B;如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发。但是,主机A未收到B发来的确认应答,也可能是因为ACK丢失了。
确认应答是一个比较理想的情况,如果网络传输过程中,出现了丢包该怎么办?
首先我们要知道丢包的原因:
如果我们把网络想象成错综复杂的公路,在公路上就会有很多收费站,正常情况如果车流量不大,车辆都能正常通过,但是如果节假日就会发生堵车的情况,这种情况车辆就没办法正常通过。在网络中我们可以把收费站理解成 “路由器/交换机” 如果数据包太多,就会在路由器/交换机中出现 “堵车” 的情况,但是路由器/交换机针对 “堵车” 的情况是比较粗暴的,它会把数据包直接丢掉,此时这个数据包就在网络上消失了。这也就是我们所说的丢包。
丢包是一个随机事件,因此在TCP传输过程中存在两种情况:
无论是哪一种情况,A都会重新传输,如果丢包的概率是10%,再重传一次,两次都丢包的概率就是1%,传输成功的概率是很大的,重传操作大幅度提升了数据传输成功率。
那么发送方何时进行重传呢?
发送方,发出数据后,会等待一段时间,如果这个时间内没有收到ack就会触发重传。
初始的等待时间是可以配置的,也可以动态变化,每经历一次超时重传,下次的等待时间就会变长。但是也不是无限变长,超时重传若干次后就会触发TCP的重置连接。
如果传输的时候ack丢了,触发了超时重传,那么接收方收到了两条一样的数据这样会不会给带来bug呢?
TCP有一个接收缓冲区(一个内存空间)会保留已经收到的数据和数据的序号,接收方如果发现,当前发送方发来的数据是已经在接收缓冲区中,接收方就会直接把这个后来的的数据丢弃掉,以确保读的时候只读到一条数据。
接收缓冲区不仅可以去重,还能进行重新排序,确保发送的顺序。
连接管理(可靠机制)
连接管理:建立连接(TCP三次握手)+ 断开连接(TCP四次挥手)
TCP三次握手:TCP在连接过程中,通信双方一共需要 “打三次招呼” 才能建立连接。
三次握手的核心作用
1.确认当前网络是否通畅
2.让发送方和接收方都知道知道的接收能力和发送能力是否正常
3.让双方在握手过程中针对一些重要的参数进行协商
TCP四次挥手:断开连接(客户端和服务器都可以发起)
TCP四次挥手可以把中间的两次合二为一吗?
不一定
不能合并的原因是ack和第二个fin触发的时机不同,ack是内核响应的(B收到fin会立即返回ack),而第二个fin是应用程序的代码触发的,从服务器收到fin(同时返回ack)再到执行到发起fin的代码,这中间要经历的时间是不确定的。
TCP三次握手的ack和第二个syn都是内核触发的,同一时机,所以可以合并。
滑动窗口(效率机制)
因为TCP的可靠传输会影响传输的效率(多出了一些等待ack的时间,单位时间内能传输的数据就少了),滑动窗口就是让可靠传输对性能的影响少一些。TCP只要引入了可靠性,传输的效率是不可能超过没有可靠性的UDP的。TCP这里的效率机制是为了缩短和UDP之间的差距。
那么TCP是如何提升效率的呢?
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000个字节(四个段)。发送前四个段的时候,不需要等待任何ACK,直接发送;收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉;窗口越大,则网络的吞吐率就越高。
如果传输过程中出现丢包怎么办呢?
如果通信双方传输的数据量比较小,也不频繁,就任然是普通的确认应答和超时重传,如果通信双方传输的数据量比较大,也比较频繁,此时就会进入滑动窗口模式,按照快速重传的方式处理。
通过滑动窗口的方式传输数据,效率会提升,窗口越大,传输效率越高(一段时间等待的ack多了,总的等待时间就少了)
但是滑动窗口并不是越大越好,如果传输速度太快,接收方就可能会处理不过来,进而接收方就会出现丢包,TCP的前提是可靠传输,在可靠性的基础上再提高传输效率。
流量控制(可靠机制)
接收方处理数据的速度是有限的。如果发送方发的太快导致接收方的缓冲区占满,这个时候如果发送方继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。站在接收方的角度,反向制约发送方的发送速度。(发送方的发送速度不应该超过接收方的处理能力)TCP支持根据接收方的处理能力,来决定发送方的发送速度,这个机制就叫做流量控制。
拥塞控制(可靠机制)
虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的。TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。
流量控制是考虑接收方的处理能力,拥塞控制是考虑通信过程中中间节点的情况。
由于中间节点结构复杂难以进行量化,所以我们可以使用 “ 实验” 来找到一个合适的值。
比如,先让A按照比较低的速度先发送数据,如果传输顺利没有丢包,就再尝试更大的窗口大小来发送数据,随着窗口的大小增大,达到一定程度,中间节点可能会出现问题,此时这个节点就可能会出现丢包,,A发现丢包了,就会重新调整窗口大小,如果发现还是丢包,就会继续缩小窗口大小,如果不丢包就尝试增大窗口大小。在这个过程中,发送方不停的调整窗口大小,逐渐达到“动态平衡” 这种做法,相当于是把中间节点都视为“整体” 通过实验的方式找到中间节点的瓶颈。
少量的丢包,我们仅仅是触发超时重传。大量的丢包,我们就认为网络拥塞。当TCP通信开始后,网络吞吐量会逐渐上升。随着网络发生拥堵,吞吐量会立刻下降。拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。
延时应答(效率机制)
A把数据传输给B,B就会立即返回ACK给A(正常情况)
A把数据传输给B,B不会立即返回ACK给A(延时应答)
延时应答本质上是为了提升传输效率,发送方的窗口大小就是传输效率的关键,流量控制是根据接收缓冲区的剩余空间来决定发送速度的,如果有办法让流量控制的窗口大小更大,发送速度更快(前提是接收方能处理过来),延时返回ack给接收方更多的时间来读取缓冲区的数据,读取缓冲区的数据之后,缓冲区的剩余空间就更大了,返回的窗口大小就更大了,传输效率就提高了。
比如说,初始情况下,接收缓冲区剩余空间是10kb,如果立即返回ack那么返回的窗口大小是10kb,如果延时一段时间返回ack,在这个过程中读取了2kb,那么此时返回的窗口大小就是12kb。
窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。
捎带应答(效率机制)
捎带应答是在延时应答的基础上又进行了效率提升,在网络通信中往往是 “一问一答”这样的通信模式。
面向字节流
当同时有多个应用层数据包被传输过去的时候就有可能出现粘包问题
那么我们如何解决粘包问题呢?
核心思路:明确应用层数据包之间的边界,①引入分隔符 ②引入长度
异常处理
进程崩溃
进程没了,程序异常终止,文件描述符表也就释放了,相当于是调用了socket.close(),此时会触发FIN,对方收到之后自然就会返回FIN和ACK(正常的四次挥手断开连接)TCP的连接可以独立于进程存在(进程没来,TCP连接不一定断开)
主机关机(正常情况)
进行关机的时候,就会先触发强制终止进程操作(相当于进程崩溃)此时就会触发FIN,对方收到之后自然就会返回FIN和ACK,不仅仅是进程没了,整个系统也可能会关闭,如果在系统关闭之前对端返回的FIN和ACK到了,此时系统还是可以返回ACK的,进行正常的四次挥手,如果系统已经关闭,FIN和ACK没收到,就无法进行后续的ACK响应了,站在对端的角度,对端会以为是自己的FIN丢包了,就会重传FIN,重传几次没有响应就会放弃连接(此时会删除对端的信息)
主机掉电(非正常情况)
主机掉电是一瞬间的事情,来不及杀进程,也来不及发送FIN,主机就直接停机了,对端不一定知道这件事。
①如果对端是在发送数据(接收方掉电),发送的数据就会一直等待ACK,触发超时重传,触发TCP的重置连接功能,会发起 “复位报文段”(TCP结构中的RST)
②如果对端是在接收数据(发送方掉电)对端还在等待数据到达,等了很久没有消息,此时是无法区分对端是没有发送消息还是对方出问题了(挂了)此时接收方也会周期性的给对方发起一个特殊的不携带业务数据的数据包,如果对方没有应答,重复了多次后任然没有响应,此时就会视为对方挂了,就会单方面释放连接。
网线断开
网线断开和主机掉电相似。