https://cloud.tencent.com/developer/article/1963303
1. 概述
当网络边缘的两台主机进行端到端的通信时,只有主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时都只用到下三层的功能
运输层有一个很重要的功能—复用和分用,发送方不同的应用进程都可以使用同一运输层协议传输数据,分用指的是接收方的运输层在剥去报文段首部把数据正确交付给目的应用程序
应用层对外服务运行后在TCP或UDP的某个端口侦听客户端的请求
1.1 运输层为相互通信的应用进程提供了逻辑通信
-
两个主机进行通信实际上就是两个主机中的应用进程互相通信。
-
应用进程之间的通信又称为端到端的通信。
-
运输层的一个很重要的功能就是复用和分用。应用层不同进程的报文通过不同的端口向下交到运输层,再往下就共用网络层提供的服务。
-
“运输层提供应用进程间的逻辑通信”。“逻辑通信”的意思是:运输层之间的通信好像是沿水平方向传送数据。但事实上这两个运输层之间并没有一条水平方向的物理连接。
1.2 运输层的主要功能
-
运输层为应用进程之间提供端到端的逻辑通信(但网络层是为主机之间提供逻辑通信)。
-
运输层还要对收到的报文进行差错检测。
-
运输层需要有两种不同的运输协议,即面向连接的 TCP 和无连接的 UDP。
1.3 运输层的两个重要协议
用户数据报协议 UDP User Datagram Protocal
UDP 在传送数据之前不需要先建立连接。对方的运输层在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 是一种最有效的工作方式
运输层的 UDP 用户数据报与网际层的IP数据报有很大区别。IP 数据报要经过互连网中许多路由器的存储转发,但 UDP 用户数据报是在运输层的端到端抽象的逻辑信道中传送的。
传输控制协议 TCP Transmssion Control Protocol
TCP 则提供面向连接的服务。TCP 不提供广播或多播服务。由于 TCP 要提供可靠的、面向连接的运输服务,因此不可避免地增加了许多的开销。这不仅使协议数据单元的首部增大很多,还要占用许多的处理机资源。
TCP 报文段是在运输层抽象的端到端逻辑信道中传送,这种信道是可靠的全双工信道。但这样的信道却不知道究竟经过了哪些路由器,而这些路由器也根本不知道上面的运输层是否建立了 TCP 连接。
1.4 运输层的端口
由于复用与分用,应用层的所有应用程序都可以通过传输层再传到网络层,运输层从网络层接收到数据后,必须分别交付给指明的各应用程序,所以给应用层的每个进程赋予一个明确的标志十分重要
但是运行在应用层的各种应用进程却不应当让计算机操作系统指派它的进程标识符。这是因为在因特网上使用的计算机的操作系统种类很多,而不同的操作系统又使用不同格式的进程标识符,进程的创建和撤销都是动态的,通信一方几乎无法识别对方机器上的进程
为了使运行不同操作系统的计算机的应用进程能够互相通信,就必须用统一的方法对 TCP/IP 体系的应用进程进行标志
运输层使用协议端口号(protocol port number),或通常简称为端口(port)。
虽然通信的终点是应用进程,但我们可以把端口想象是通信的终点,因为我们只要把要传送的报文交到目的主机的某一个合适的目的端口,剩下的工作(即最后交付目的进程)就由 TCP 来完成
也就是说一个应用程序,一个进程规定一个端口,一个计算计上打开的不同进程端口号不能相同,网页服务器的访问端口是80端口
注意:
-
在协议栈层间的抽象的协议端口是软件端口。
-
路由器或交换机上的端口是硬件端口。
-
硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层的各种协议进程与运输实体进行层间交互的一种地址
-
端口用一个 16 位端口号进行标志。
-
端口号只具有本地意义,即端口号只是为了标志本计算机应用层中的各进程。在因特网中不同计算机的相同端口号是没有联系的。
三类端口
① 熟知端口:数值一般为 0~1023,指派给TCP/IP最重要的一些应用程序
http:TCP 80端口
https:TCP 443端口
ftp:TCP 21端口
SMTP:TCP 25端口
共享文件夹:TCP 445端口
DNS:UDP 53端口
SQL:TCP 1433端口
② 登记端口号:数值为1024~49151,为没有熟知端口号的应用程序使用的。使用这个范围的端口号必须在 IANA 登记,以防止重复
③ 客户端口号或短暂端口号:数值为49152~65535,留给客户进程选择暂时使用。当服务器进程收到客户进程的报文时,就知道了客户进程所使用的动态端口号。通信结束后,这个端口号可供其他客户进程以后使用
你可以发现,当我们打开一个浏览器,浏览器里面打开多个窗口,那么一个窗口就和服务器建立了一个会话,不同的窗口源端口是不同的,这样才能保证不同网页正常的显示
浏览器是多进程的,有一个主控进程,以及每一个tab页面都会新开一个进程(某些情况下多个tab会合并进程)
1.5 判断是UDP还是TCP
QQ聊天使用的是UDP
不用建立连接,因为你并不知道两个人要说多久,而且虽然UDP不保证可靠性但是QQ尝试发送,没有发送成功会给用户反馈
QQ传输文件使用的是TCP
DNS是UDP
访问网站是TCP
1.6 windows防火墙的作用
为了让网络安全,我只要把我网络的所有端口都关闭别人就访问不到我,windows防火墙能让端口动态打开关闭,也就是我想和外面交流那么流量进出,不通信别人直接访问我就不行
所以我打开防火墙我就在网络上隐身了,被人想Ping我也Ping不通
但是木马程序windows防火墙控制不了,因为windows防火墙的本质是我主动连接外面可以,别人主动连接我就不行,木马病毒是在我的电脑上安装了一个软件主动访问了外面,这样防火墙就允许外部就可以访问我了
2. 用户数据报协议UDP
2.1 UDP概述
UDP 只在 IP 的数据报服务之上增加了很少一点的功能,即端口的功能(复用和分用)和差错检测的功能
-
UDP 是无连接的,即发送数据之前不需要建立连接。
-
UDP 使用尽最大努力交付,即不保证可靠交付
-
UDP 是面向报文的。UDP 没有拥塞控制,很适合多媒体通信的要求。
发送方 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
应用层交给 UDP 多长的报文,UDP 就照样发送,即一次发送一个报文。
接收方 UDP 对 IP 层交上来的 UDP 用户数据报,在去除首部后就原封不动地交付上层的应用进程,一次交付一个完整的报文。
应用程序必须选择合适大小的报文,如果报文太长,UDP把他交付给IP层,IP层要进行分片,这会降低IP层的效率;如果太短,也会降低效率
-
UDP 支持一对一、一对多、多对一和多对多的交互通信。
-
不使用拥塞控制
-
UDP 的首部开销小,只有 8 个字节。
2.2 UDP的首部格式
在计算检验和时,临时把“伪首部”和 UDP 用户数据报连接在一起。伪首部仅仅是为了计算检验和。
3. 传输控制协议TCP
3.1 TCP的主要特点
-
TCP 是面向连接的运输层协议
会先确保网络是通的,三次握手进行通信 -
每一条 TCP 连接只能有两个端点(endpoint),每一条 TCP 连接只能是点对点的(一对一)
不向UDP可以多播广播 -
TCP 提供可靠交付的服务。
通过TCP连接传输的数据无差错,不丢失,不充分,并且按序到达 -
TCP 提供全双工通信
TCP允许双方的应用程序在任何时候都能发送数据,TCP连接的两端都设有发送缓存和接收缓存,用来存放双方通信的数据
全双工还有一点就是A和B通信,A一直给B发数据,B接收,TCP也需要让A知道B一直在接收,也就是在A发送的时候让B也给A一个反馈 -
面向字节流
TCP 对应用进程一次把多长的报文发送到TCP 的缓存中是不关心的。
TCP 根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。
TCP 可把太长的数据块划分短一些再传送。TCP 也可等待积累有足够多的字节后再构成报文段发送出去
3.2 TCP连接
每一条TCP连接有两个端点,就是套接字,端口号拼接到(contatenated with) IP 地址即构成了套接字
4. 可靠传输的工作原理
IP层只能提供尽最大努力服务,也就是说网络层提供的是不可靠传输,TCP必须要采用适当的措施才能使两个运输层之间的通信变得可靠
① 传输信道不产生差错
② 不管发送方以多块的速度发送数据,接收方总来得及处理收到的数据
4.1 停止等待协议
ARQ自动重传请求
每发送完一个分组就停止发送,等待对方的确认,在收到确认后再发送下一个分组
注意
- 在发送完一个分组后,必须暂时保留已发送的分组的副本。
- 分组和确认分组都必须进行编号。
- 超时计时器的重传时间应当比数据在分组传输的平均往返时间更长一些。
确认丢失和确认迟到
ARQ 表明重传的请求是自动进行的。接收方不需要请求发送方重传某个出错的分组 ,而是发送方发现时间过了自己自动重传
信道利用率
停止等待协议的优点是简单,但缺点是信道利用率太低
**为了改进可以使用流水线传输 **
发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认,也就是下面将要提到的连续ARQ协议
4.2 连续ARQ协议
滑动窗口
发送方维持一个发送窗口,位于发送窗口的前五个分组都可以连续发送出去而不需要等待对方的确认,发送完5个后等待确认,收到一个确认后发送窗口向前滑动,并把确认了的缓存中的包清除
累积确认
接收方一般采用累积确认的方式。即不必对收到的分组逐个发送确认,而是对按序到达的最后一个分组发送确认,这样就表示:到这个分组为止的所有分组都已正确收到了
累积确认有的优点是:容易实现,即使确认丢失也不必重传。缺点是:不能向发送方反映出接收方已经正确收到的所有分组的信息
回退N步
如果发送方发送了前 5 个分组,而中间的第 3 个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。
这就叫做 Go-back-N(回退 N),表示需要再退回来重传已发送过的 N 个分组。
可见当通信线路质量不好时,连续 ARQ 协议会带来负面的影响
5. TCP报文段的首部格式
**源端口和目的端口字段:**各占 2 字节。端口是运输层与应用层的服务接口。运输层的复用和分用功能都要通过端口才能实现
**序号字段:**占 4 字节。TCP 连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号
TCP是面向字节流的,从TCP缓存取出字节然后编号发送
**确认号字段:**占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。 告诉对方下一个应该从第几个字节开始发送(因为我们是面向流的有序发送)
**数据偏移(即首部长度):**占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。“数据偏移”的单位是 32 位字(以 4 字节为计算单位),也就是告诉我们首部有多长
**保留字段:**占 6 位,保留为今后使用,但目前应置为 0
**紧急 URG:**当 URG = 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)
**确认 ACK:**只有当 ACK =1 时确认号字段才有效。当 ACK = 0 时,确认号无效,也就是在最开始建立会话的时候,请求建立连接的发送的数据的ACK就是0,因为确认号是对以前发送数据的确认,而以前还没有发送数据当然就不需要确认
除了最初建立连接时的 SYN 包之外该位必须设为 1;
**推送 PSH (PuSH):**接收 TCP 收到 PSH = 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付
比如再传送文件的时候还没传送完,我们想让他停止,那么就需要发送一个停止的数据,但是按照原先的按照顺序发送,这个数据报只能排队在最后,也就达不到停止的作用,这个时候就需要设置这个标记位先传停止命令
**复位 RST (ReSeT):**当 RST = 1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接
**同步 SYN:**用于建立连接,该位设为 1,表示希望建立连接,并在其序列号的字段进行序列号初值设定;
**终止 FIN (FINis):**用来释放一个连接。FIN = 1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接
每个主机又对对方的 FIN 包进行确认应答之后可以断开连接。不过,主机收到 FIN 设置为 1 的 TCP 段之后不必马上回复一个 FIN 包,而是可以等到缓冲区中的所有数据都因为已成功发送而被自动删除之后再发 FIN 包
**窗口字段:**占 2 字节,用来让对方设置发送窗口的依据,单位为字节
这个字段很重要,窗口指的是发送本报文段的一方的接收窗口,这个值告诉对方,从本报文的首部中的确认号算起,接收方还允许对方发送多少字节
之所以有这个限制是因为接收方的数据缓存空间是有限的
窗口字段明确的指出了现在允许对方发送的数据量,窗口在动态变化
**检验和:**占 2 字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部
6. TCP可靠传输的实现
假设A发送数据,B给出确认
6.1 以字节为单位的滑动窗口
TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来
-
接收端LastByteRead指向了TCP缓冲区中读到的位置,NextByteExpected指向的地方是收到的连续包的最后一个位置,LastByteRcved指向的是收到的包的最后一个位置,我们可以看到中间有些数据还没有到达,所以有数据空白区。
-
发送端的LastByteAcked指向了被接收端Ack过的位置(表示成功发送确认),LastByteSent表示发出去了,但还没有收到成功确认的Ack,LastByteWritten指向的是上层应用正在写的地方
6.2 超时重传时间
TCP的发送方在规定时间没有接收到确认就要重传
6.3 选择确认SACK
如果接收到的报文的无差错,只是中间缺少了一些序号的数据,那么我们希望只传送缺少的数据而不重传已经正确到达接收方的数据
接收方收到了和前面的字节流不连续的两个字节块。
如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。
现在大多数的实现还是重传所有未被确认的数据块
7. TCP流量控制
7.1 利用滑动窗口实现流量控制
其实流量控制就是接收方接收的数据太多,但是上层程序还没读取,那么就要通知发送方,把发送窗口变小
一般来说我们希望数据传输的更快,但是如果发送方把数据发送的过快啊,接收方可能来不及接收(缓存满了),造成数据的丢失
流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收
利用滑动窗口机制可以很方便地在 TCP 连接上实现流量控制
A 向 B 发送数据。在连接建立时,B 告诉 A:我的接收窗口 rwnd = 400(字节)
8. TCP拥塞控制
网络中使用网络的所有计算机如何防止网络的堵塞
TCP 主要通过四个算法来进行拥塞控制:
慢开始、拥塞避免、快重传、快恢复
8.1 拥塞控制的一般原理
在某段时间,若对网络中某资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏——产生拥塞(congestion)
资源:计算机网络中的链路容量(带宽),交换节点(路由器)中的缓存和处理机等,都是网络资源
对资源需求的总和 > 可用资源
若网络中有许多资源同时产生拥塞,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降
为什么单纯的增加网络上的资源不能解决拥塞
只有网络中所有的部分都平衡了,问题才能得到解决
假设我们仅仅把交换节点的缓存变大,这也就能容纳更多的数据,但是输出链路和处理机的速度没有得到增加,那么队列中分组的排队等待时间也会增加,发送方接收不到确认响应只能重传,然而这些数据只是在排队而已并不是丢弃了,这更造成了网络资源的浪费
流量控制和拥塞控制的区别
拥塞控制是一个全局性的过程,涉及到所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。
流量控制往往指在给定的发送端和接收端之间的点对点通信量的控制。 流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收
**理想情况:**超过链路吞吐量的会被链路丢弃
**没有拥塞控制:**由于发送给路由器的数据包太多了,路由器处理不了这么多就可能出现丢包,甚至死机
**有了拥塞控制:**解决了没有拥塞控制时的死锁局面
8.1 慢开始和拥塞避免
发送方维持一个叫做拥塞窗口 cwnd (congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。
注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口
发送方让自己的发送窗口等于拥塞窗口。如再考虑到接收方的接收能力,则发送窗口还可能小于拥塞窗口。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞(表现在发送方没有收到确认的报文,出现了超时重传,那就有可能是发生了拥塞),拥塞窗口就减小一些,以减少注入到网络中的分组数
在主机刚刚开始发送报文段时可先设置拥塞窗口 cwnd = 1,即设置为一个最大报文段 MSS 的数值。
在每收到一个对新的报文段的确认后,将拥塞窗口加 1,即增加一个 MSS 的数值。
用这样的方法逐步增大发送端的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理
为了防止拥塞窗口cwnd增长过大而引起网络拥塞,设置一个慢开始门限ssthresh状态变量
- 当 cwnd < ssthresh 时,使用慢开始算法。
- 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
- 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法。
- 拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍,使拥塞窗口 cwnd 按线性规律缓慢增长。
拥塞避免并不能完全的避免阻塞,而是把拥塞窗口控制位按线性规律增长,使网络比较不容易出现阻塞
无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送方窗口值的一半(但不能小于2)
然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。
这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕
8.2 快速重传和快速恢复
有时候个别报文段会在网络中丢失,但是实际上网络并没有发生阻塞拥塞,如果发送方迟迟收不到确认会以为网络阻塞然后启动满开始,降低了传输效率
-
快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认。这样做可以让发送方及早知道有报文段没有到达接收方。
-
发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段。
-
不难看出,快重传并非取消重传计时器,而是在某些情况下可更早地重传丢失的报文段
(1) 当发送端收到连续三个重复的确认时,就执行“乘法减小”算法,把慢开始门限 ssthresh 减半。但接下去不执行慢开始算法。
(2)由于发送方现在认为网络很可能没有发生拥塞,因此现在不执行慢开始算法,即拥塞窗口 cwnd 现在不设置为 1,而是设置为慢开始门限 ssthresh 减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
- 执行 乘法减小 算法:把 慢开始门限(ssthresh)设置为 出现拥塞时发送方窗口值的一半 = 拥塞窗口的1半
- 将拥塞窗口(cwnd)值设置为 慢开始门限ssthresh减半后的数值 = 拥塞窗口的1半
- 执行 加法增大 算法:执行拥塞避免算法,使拥塞窗口缓慢地线性增大。
9. TCP的运输连接管理
TCP是面向连接的协议
运输连接就是用来传送TCP报文的
运输连接就有三个阶段,即:连接建立、数据传送和连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行
TCP连接建立的过程需要解决以下三个问题
-
要使每一方能够确知对方的存在。
-
要允许双方协商一些参数(如最大报文段长度,最大窗口大小,服务质量等)。
-
能够对运输实体资源(如缓存大小,连接表中的项目等)进行分配。
9.1 TCP连接的建立
TCP连接的过程叫做握手,握手需要在客户机(主动建立连接的应用进程)和服务器(等待建立连接的一方)交换三个TCP报文段
首先 B 处于 LISTEN(监听)状态,等待客户的连接请求,A 向 B 发送连接请求报文,SYN=1,ACK=0,选择一个初始的序号 x
TCP规定SYN数据段不能携带数据,但是要消耗一个序号,这时TCP客户端进程进入SYN-SENT(同步已发送状态)
B 收到连接请求报文,如果同意建立连接,则向 A 发送连接确认报文,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y
A 收到 B 的连接确认报文后,还要向 B 发出确认,确认号为 y+1,序号为 x+1
注意上述过程中,B向A发送的报文段也可以拆分为两个报文段
① 确认报文段(ACK = 1, ack = x+1)
② 同步报文段(SYN = 1, seq = y)
一个来回就足以证明网络的通畅了,为什么A最后还要发送一次确认呢
“3次握手”的作用就是双方都能明确自己和对方的收、发能力是正常的。
防止已经失效的连接请求报文突然又传送到了B
假设没有最后一次确认,A发送了一个连接请求,但是这个请求报文段在链路上延迟了,这个时候A关闭了连接,然后那个延迟的报文段到达了B,B发现这个是请求连接的报文,立马发送了同意链接的确认报文,但是A这个时候已经关闭了连接,自然连接建立失败,但是因为没有A向B发送确认的这个过程,B以为建立了连接,导致很多的资源被浪费
总结一下三次握手
第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力,服务端的发送、接收能力是正常的
9.2 TCP连接的释放
为什么要四次挥手
-
TCP连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。
-
这时对方会回一个ACK,此时一个方向的连接关闭。但是另一个方向仍然可以继续传输数据,也就是说,服务端收到客户端的 FIN 标志,知道客户端想要断开这次连接了,但是,我服务端,我还想发数据呢?我等到发送完了所有的数据后,会发送一个 FIN 段来关闭此方向上的连接。接收方发送 ACK确认关闭连接。注意,接收到FIN报文的一方只能回复一个ACK, 它是无法马上返回对方一个FIN报文段的,因为结束数据传输的“指令”是上层应用层给出的,我只是一个“搬运工”,我无法了解“上层的意志”。
-
客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。
-
因为服务端在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,把 ACK 和 SYN 放在一个报文里发送给客户端。而关闭连接时,当收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方 ACK 和 FIN 一般都会分开发
10. TCP 短连接和长连接的区别
短连接: Client 向 Server 发送消息,Server 回应 Client,然后一次读写就完成了,这时候双方任何一个都可以发起 close 操作,不过一般都是 Client 先发起 close 操作。短连接一般只会在 Client/Server 间传递一次读写操作。
短连接的优点: 管理起来比较简单,建立存在的连接都是有用的连接,不需要额外的控制手段。
长连接: Client 与 Server 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。
在长连接的应用场景下,Client 端一般不会主动关闭它们之间的连接,Client 与 Server 之间的连接如果一直不关闭的话,随着客户端连接越来越多,Server 压力也越来越大,这时候 Server 端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致 Server 端服务受损;如果条件再允许可以以客户端为颗粒度,限制每个客户端的最大长连接数,从而避免某个客户端连累后端的服务
11. TCP粘包和拆包
11.1 基本介绍
因为tcp协议是基于流的传输层协议,他会根据tcp缓冲区的实际情况对数据包进行拆分或合并,所以对于上层应用来说,会发生一个大的数据包被tcp拆分成多个小的数据包或多个小的数据包被tcp合并成一个大的数据包的情况
假设 Client 向 Server 连续发送了两个数据包,用 packet1 和 packet2 来表示,那么服务端收到的数据可以分为三种情况,现列举如下
第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象
第二种情况,接收端只收到一个数据包,但是这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理
第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的
后面两种情况也就是所谓的粘包、拆包
11.2 发生拆包和粘包的主要原因
- 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包
- 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包
- 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包
- 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包
11.3 解决方案
- 定长消息
发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓 冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来
- 分隔符分割
可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开
- 消息头中标识消息长度
发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了