传输层重点协议----TCP
-
- 1、TCP
-
- [① TCP协议段格式](#① TCP协议段格式)
- [② TCP原理](#② TCP原理)
-
- [Ⅰ 确认应答机制(可靠性)](#Ⅰ 确认应答机制(可靠性))
- [Ⅱ 超时重传机制(可靠性)](#Ⅱ 超时重传机制(可靠性))
- [Ⅲ 连接管理机制(可靠性)](#Ⅲ 连接管理机制(可靠性))
- [Ⅳ 滑动窗口机制(效率)](#Ⅳ 滑动窗口机制(效率))
- [Ⅴ 流量控制机制(可靠性)](#Ⅴ 流量控制机制(可靠性))
- [Ⅵ 拥塞控制机制(可靠性)](#Ⅵ 拥塞控制机制(可靠性))
- [Ⅶ 延迟应答机制(效率)](#Ⅶ 延迟应答机制(效率))
- [Ⅷ 捎带应答机制(效率)](#Ⅷ 捎带应答机制(效率))
- [Ⅸ 面向字节流](#Ⅸ 面向字节流)
- [Ⅹ 粘包问题](#Ⅹ 粘包问题)
- [③ TCP异常情况](#③ TCP异常情况)
-
- [Ⅰ 主机关机(按照固定程序关机)](#Ⅰ 主机关机(按照固定程序关机))
- [Ⅱ 程序崩溃](#Ⅱ 程序崩溃)
- [Ⅲ 主机掉电(突然拔电源)](#Ⅲ 主机掉电(突然拔电源))
- [Ⅳ 网线断开](#Ⅳ 网线断开)
- [④ TCP小结](#④ TCP小结)
- [⑤ 基于TCP的应用层协议](#⑤ 基于TCP的应用层协议)
1、TCP
TCP,即Transmission Control Protocol,传输控制协议。人如其名,要对数据的传输进行一个详细的控制。
① TCP协议段格式
- 源/目的端口号:表示数据是从哪个进程来,到哪个进程去;
- 32位序号/32位确认号:给发送个每一个数据都进行编号;如果当前报文是普通报文,则确认序号不生效
- 4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是 15 * 4 = 60
- 6位标志位:
- URG:紧急指针是否有效
- ACK:确认号是否有效(是否为应答报文)
- PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
- RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
- SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
- FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
- 16位窗口大小:后面再说
- 16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光包含TCP首部,也包含TCP数据部分。
- 16位紧急指针:标识哪部分数据是紧急数据;
- 40字节头部选项:暂时忽略;
② TCP原理
TCP对数据传输提供的管控机制,主要体现在两个方面:可靠和效率。
这些机制和多线程的设计原则类似:保证数据传输可靠的前提下,尽可能的提高传输效率。
Ⅰ 确认应答机制(可靠性)
一口气发了 1000 个字节的数据(一个 TCP 数据报,长度是 1000,序号是 1)
应答报文中的确认序号,就是 1001.
应答报文:可视为只有 TCP 报头,没有载荷.
意思就是 < 1001 的数据,B 已经收到了;接下来 A 要从 1001 开始往后发送!!!
Ⅱ 超时重传机制(可靠性)
在确认应答的情况下,如果没有收到 ACK,就需要通过其他途径来处理!
超时重传 :不是说没有收到 ACK,立即就放弃,就需要重新再发一遍!
网络的环境是非常复杂的,尤其是有些时候,网络会拥堵,拥堵就可能会导致丢包 !
丢包,是 "无差别" 丢的。任何一个数据报,都有可能会丢包!
普通报文 可能丢失,ACK 也可能丢失!
业务数据丢失
发送方等待一定的时间之后,没有收到 ACK 就会重传!
ACK 丢失
业务数据已经到了 主机 B 了,反馈的 ACK 没有回过去,发送方等待一会之后,就触发了重传!
- 对于 发送方 来说,无法区分是 业务数据丢失,还是 ACK 丢失。因此 发送方 能做的只是,达到一定时间之后,就重传!
- 对于 接收方 来说,会根据序号,来进行数据去重。接收方 知道自己都收到了哪些数据,当发现又收到了一份之前的数据就会自动丢弃,保证应用层读到的数据是不重复的!
假设第一次重传失败了,进行第二次重传,又失败了;
第一次丢包超时时间为 t1,第二次丢包超时时间为 t2,此时一定:t1 < t2
丢包超时等待的时间间隔,随着丢包此时的增加,越来越大!
Ⅲ 连接管理机制(可靠性)
确认应答 和 超时重传 可以认为是保证 TCP 可靠性的 最核心机制!
但是 连接管理 是面试中最高频的问题(网络知识)!
在正常情况下,TCP 要经过 三次握手 建立连接,四次挥手 断开连接!
三次握手:必须是客户端主动,服务器被动
三次握手的意义:(三次握手,认为是一种保证可靠性的机制)
- 这个东西相当于 "投石问路",在正式通信之前,先确定好通信链路是否畅通!
如果通信链路不畅通,后续大概率要丢包! - 能够让通信双方协商一些重要的参数!(比如:序号是从几开始的;MSS 等等)
四次挥手:客户端和服务器都可以主动
与 三次握手 不同,四次挥手 看起来也是双方各自给对方发送 FIN,各自给对方发送 ACK。
但是,四次挥手 这里的 中间两次,不能合并!
TCP 的状态:
- LISTEN :(服务器的状态)服务器已经启动完毕,已经绑定端口成功!
例如:手机开机完毕,信号良好(随时有人给他打电话) - ESTABLISHED:连接建立好了,可以进行后续通信!
例如:打电话号码拨通,对方已经接听,接下来就可以随时说话了 - CLOSE_WAIT:(被动接受 FIN 的一方,进入 CLOSE_WAIT)这个状态就是自己收到了 FIN 也返回了 ACK,在自己发送 FIN 之前,处于的状态。(等待代码调用 close 方法,发送 FIN)
- TIME_WAIT:(主动发起 FIN 的一方,会进入 TIME_WAIT)主动的一方,收到对方的 FIN,并返回 ACK 之后,就会进入这个状态,而不是直接进入 CLOSED 状态,在 TIME_WAIT 状态下停留一段时间,才会进入 CLOSED 彻底释放连接。
(防止最后一个 ACK 丢失,万一最后一个 ACK 丢失,在 TIME_WAIT 状态下,还可以重传)
Ⅳ 滑动窗口机制(效率)
我们要清楚一点可靠性 和 效率是冲突的 保证 可靠性 肯定会影响到 效率 的。
TCP 在保证 可靠性 的前提下,尽可能的提高 效率。
滑动窗口机制的本质 就是把等待 ACK 的时间重叠起来。
减少等待时间,就相当于提高了效率。
每次传输,都需要等待 ACK,收到 ACK 再发下一题数据,这样效率就很低。
我们一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000 个字节(四个段)。
发送前四个段的时候,不需要等待任何ACK,直接发送;
收到第一个 ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;
操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有 应答;只有确认应答过的数据,才能从缓冲区删掉;
窗口越大,则网络的吞吐率就越高;
对于超时重传丢包,这里分两种情况讨论。
-
情况一:ACK 丢失
这种情况下,部分 ACK 丢了并不要紧,因为可以通过后续的 ACK 进行确认;
-
情况二:数据包丢失
主机 A 发了半天之后,看到了连续的好几个 1001,它就明白了,是 1001 这个数据丢了!
接下来,A 就会重传 1001 这个数据了。
此处的原则是:哪条丢了,就重传哪条;已经传输到的数据,不必重复传输!
Ⅴ 流量控制机制(可靠性)
滑动窗口,窗口大小越大,发送速率就越快!流量控制,就是在针对发送速率进行制约!
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应,反而还降低了速率。
流量控制,就是通过 接收缓冲区 剩余空间大小,来作为下一次发送时候的窗口大小。
Ⅵ 拥塞控制机制(可靠性)
流量控制,站在接收方的角度,来控制发送速率;
但是整体的传输,其实不光有发送方和接收方,还有中间的一系列用来转发的设备。
拥塞控制,采取的方法:做实验。
通过实验的方式,找出一个合适的窗口大小。
刚开始按照小的窗口来发送。
如果不丢包,说明网络中间环境 比较畅通,就可以逐渐放大发送窗口的大小。
放大到一定程度,速率已经比较快,网络上就容易出现拥堵,进一步出现丢包;当发送方发现丢包之后,就减小发送的窗口。
反复在 2 和 3 之间循环!
这个过程,就达到了一个 "动态平衡",发送速率不慢,接近了能承载的极限,同时还可以尽量少丢包,还能适应网络环境的动态变化。
流量控制 和 拥塞控制 都能影响发送方的滑动窗口大小!最终的滑动窗口大小,就取决于两者的较小值。
像上面这样的拥塞窗口增长速度,是指数级别的。"慢启动" 只是指初使时慢,但是增长速度非常快。
为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。
此处引入一个叫做慢启动的阈值
当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。
Ⅶ 延迟应答机制(效率)
延时应答也是一个用来 提高效率的机制。
延时应答,则是让窗口能够大一些。
在流量控制中,通过 ACK 告知发送方,窗口大小是多少合适,这里的窗口大小是由接收缓冲区的空余空间来决定的。
如果立即返回 ACK,可能接收端缓冲区空余空间为 5KB;
稍等一会(例如 500 ms),在这个过程中,应用程序不断的取走数据,接收端缓冲区空余空间可能为 100 KB 了。
Ⅷ 捎带应答机制(效率)
正常来说,ACK 是收到请求之后,内核立即返回的;而响应数据,则是应用程序代码,发送的。二者是不同的时机,就不能把 ACK 和 响应报文 合并。
但是基于延时应答等的基础上,延时一会,就可能和返回 响应数据,时间上重合了。
响应在收到请求之后,多长时间之内返回?不确定:
- 可能快:本来也要发送 数据报文,ACK 就搭个顺风车;
- 可能慢:ACK 就先走了。
Ⅸ 面向字节流
面向字节流,指的是读写载荷数据的时候,是按照 "字节流" 的方式来读取的。
TCP 数据报,本身仍然是 一个一个 "数据报" 这样的方式来传输的。
(应用程序,是感受不到从哪里到哪里是一个数据报的)
传输数据的时候,数据会先写入 接收缓冲区,应用程序直接从 接收缓冲区 取数据。
此时,应用程序在读取数据的时候,就可以灵活的进行了,可以一次读 M 个字节,分 N 次读。
Ⅹ 粘包问题
如果一个 TCP 连接,里边只传一个 应用层数据包,这个时候不会粘包(短链接)。
如果一个 TCP 连接,传输多个应用层数据包,这个时候就容易分不清,从哪到哪是一个完整了应用层数据(长连接)。这就是粘包问题。
不仅仅是 TCP ,只要是面向字节流的传输,都有粘包问题(文件传输)。
粘包问题的解决方案:在应用程序代码中,明确包之间的边界!
- 使用分隔符
- 约定长度
③ TCP异常情况
Ⅰ 主机关机(按照固定程序关机)
按照程序关机,会先杀死所有的用户进程(也就包括我们自己写的 TCP 程序)
杀死进程 => 释放进程 PCB => 释放文件描述符表上对应的文件资源(相当于调用 close)
这个时候就会出发 FIN,开启 四次挥手 的流程!
如果四次挥手挥完了,就继续关机;
如果还没有挥完,就已经关机了,对端重传 FIN 若干次,没有响应,就放弃了。
Ⅱ 程序崩溃
同上,程序是正常关闭,还是异常崩溃,都会释放 PCB,都会释放文件描述符表(相当于调用 close)
也还是会正常 四次挥手(虽然进程没了,但是本身 TCP 连接也是内核负责,内核仍然会继续完成后续的挥手过程)
Ⅲ 主机掉电(突然拔电源)
笔记本还好(内置电源),如果是台式机之类的,直接没了,肯定来不及挥手。
- 接收方掉电:对方尝试发送数据,发现没有 ACK,尝试重传,重传几次,仍然没有 ACK,发送方尝试重新建立连接。如果重新建立也不成,认为是当前网络上出现了严重问题,就自然放弃了。
- 发送方掉电:接收方就在等待发送方发的数据,由于发送方没了,这个数据显然发不过来了,接收方 也不知道是 发送方 没有发,还是 发送方 出现异常了(接收方区分不了)。
Ⅳ 网线断开
④ TCP小结
可靠性:
- 校验和
- 序列号(按序到达)
- 确认应答
- 超时重发
- 连接管理
- 流量控制
- 拥塞控制
提高性能:
- 滑动窗口
- 快速重传
- 延迟应答
- 捎带应答
其他:
- 定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等)
⑤ 基于TCP的应用层协议
- HTTP
- HTTPS
- SSH
- Telnet
- FTP
- SMTP
当然,也包括你自己写TCP程序时自定义的应用层协议;