传输层协议:TCP

报头

TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,位于OSI模型的第四层。它通过三次握手建立连接,确保数据有序、无差错地传输,并提供流量控制和拥塞控制机制。

我们学习TCP的各种其实就是学习其报头的:

那么现在我来逐个讲解其报头含义。

源端口号和目的端口号

这两个应当是非常直接的。TCP/IP模型采用五元组的形式标记一个通信,其中就需要到源端口号和目的端口号。

4位首部长度

除了选项以外 就是我们TCP报头的基本大小,他是固定的20字节。但是选项的大小是不固定的,可以没有也可以有。因此我们需要一个数据记录报头的大小,这个数据就是4位首部长度。

4位首首部长度的基本单位是4字节,因此它可以表示0-60字节。这说明没有选项时,默认的首部长度位0101。选项最多为40字节。

ACK 确认应答机制

在保证TCP可靠的机制中,确认应答机制是十分重要的一个。

我们在日常生活中聊天:

当我们向对方发起聊天时,并不能确保对方听到了我们说的话,我们需要对方的应答如语言应答:

这时候左边就知道对方听到了他说的第一句话,但这时候问题来了,右边无法确定左边是否听见了自己的应答。因此左边又要对右边进行应答:

同样的,对方也要应答左边的这句话,才能让左边确保对方收到了信息。

这样就陷入了死循环,也就是实际通信过程中,总有最新的一条应答是无法保证有效的。

于是我们考虑不保证应答,只保证收到应答的单边通信情况:

如上我们只考虑客户端向服务器单方向发送信息的情况时,我们只需要客户端收到服务器的应答就能确保服务器一定收到了客户端的信息,这样就确保了单向的可靠性。我们无需确保服务器知道客户端一定收到了应答。

双向通信的时候也是类似考虑。

这就是确认应答机制。报头中的ACK,即Acknowledgement标记位就是标记这组报文是否为应答报文。

序号和确认序号

我们TCP通信有两种情况,一种是上述的串行发送:

也可以是并行发送:

也就是客户端在等待应答的过程中也可以继续发送信息。事实上,这种发送情况才是常有的。

但这时就有一个问题,我怎么知道你的应答是对应哪一条发送信息呢?

因此我们需要给发送信息和应答一个标记。

首先是发送信息时,由于TCP是面向字节流的,发送信息时可以给数据的每一个字节一个编号(从1开始),这个就是序列号。

发送数据方要在报头的序号部分填发送数据的最后一个字节的序号,ack则要在确认序号部分填上收到的序号+1.

例如:客户端现在要发送3000字节的数据给服务器,分三次发送,因此三次发送的序列号分别就是1000,2000,3000.服务器要回应的确认序号就是1001,2001,3001:

这里还有一些确认序号代表的是确认序号前的数据已接收,例如1001就代表已经接受了前1000个序号的数据。

这意味着,如果1001-2000的数据丢失,服务器只收到了1-1000和2001-3000的数据,应答报文的确认序号就只有1001.

同时序号和确认序号的存在告诉我们每次通信传输的数据有一个绝对上限:232Byte=4GB,不过正常也用不了这么大。

捎带应答

刚才我们的序号机制和确认序号机制,明明可以只用一套序号,为什么非要搞两个序号出来,这不浪费存储吗?

实际上,我们刚刚讲的都是单向通信,但是我们实际情况是经常出现双向通信。当客户端向服务器写入数据时,服务器也能向客户端写入数据。

这时候就能将服务器要发送的数据和应答报头合并,这样能节省一个报头的通信开销,提高效率:

ps:这里报头少打了个RST

如上服务器向客户端发送200字节的数据捎带应答客户端发送的1000字节数据,就可以在序号里填200,确认序号里填1001.

这就是捎带应答机制。

超时重传

客户端向服务器发送数据没有收到应答有两种情况,一是数据丢失:

二是应答丢失:

可不管是哪种丢失,在TCP协议看来,没有应答=没收到数据。因此在发送数据之后tcp内部就会维护一个倒计时,当这个数段内没有收到应答就会重复发送。

所以服务器是很容易收到重复数据的,再根据前面的序号我们就能实现数据去重

那么,如果超时的时间如何确定?

• 最理想的情况下,找到一个最小的时间,保证"确认应答一定能在这个时间内返回".

• 但是这个时间的长短,随着网络环境的不同,是有差异的.

• 如果超时时间设的太长,会影响整体的重传效率;

• 如果超时时间设的太短,有可能会频繁发送重复的包;

TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算 这个最大超时时间.

• Linux中(BSDUnix和Windows也是如此),超时以500ms 为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍.

• 如果重发一次之后,仍然得不到应答,等待2500ms后再进行重传.
• 如果仍然得不到应答,等待4
500ms进行重传.依次类推,以指数形式递增.

• 累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接.

连接管理机制

TCP的连接管理机制也是其非常复杂的一套机制,正常来说TCP要经过三次握手建立连接,四次挥手断开连接。

三次握手

我们先看三次握手过程:

这里CLOSED、LISTEN、SYN_SENT、SYN_RCVD、ESTABLISHED都是内核维护套接字和连接关系中,连接的状态。总之就是一个宏定义的状态。

联测对用的时应用层的逻辑。

首先通信双方建立连接必须征得双方的意愿。

因此建立连接发起方(通常是客户端)就会在TCP报头上标记SYN(Synchronize Sequence Number,同步序列号),发送给远端,并将连接状态标记为SYN_SENT。

服务器收到了客户端的连接请求,此时服务器是LISTEN状态,属于是来者不拒,因此他必定同意建立连接 (这里就区分断开连接时不是必定同意),必定会应答。同时他也要发送请求建立连接,因此用到捎带应答机制,同时给SYN和ACK标记。

注意当客户端收到了服务器的ACK之后,连接状态就标记成了ESTABLISHED,意思是客户端认为连接已经建立。而服务器要等到客户端的ACK才能确定连接建立。
客户端和服务器的连接建立有时间差

这意味着三次握手其实在赌最后一次ACK对方一定收到了。

这里还要说明一下,三次握手是内核级操作,connect和accept这些函数不参与三次握手,只是发起者。也算是一种常见的黑箱机制。

RST

三次握手真是一场豪赌,但要是真的没有收到最后一次ACK呢:

这时候客户端认为已经建立连接了,就会发送数据。当服务器接收到数据就会知道,对方以为连接建立了,因此服务器会发送应答+重新建立连接的请求,也就是ACK+RST:

这时客户端收到RST就会开始重新三次握手。

不止这个场景。网络通信什么意外都有可能发生,意外断开连接常有,所以RST也是常有的事。

为什么要三次握手(面试常考题)

其实我们已经知道为什么要三次握手,但脑子里没有一个系统的答案,我这里来谈谈原因。

首先讲一个不是重要的原因,就是一次握手和两次握手有明显的缺陷。

我们知道内核要维护连接状态,因此要消耗一定内存。

如果客户端发送一堆SYN请求,(SYN洪水),就会消耗服务器的大量空间维护连接状态。

三次握手并不能有效防止 SYN洪水,但能避免这么明显的缺陷。

事实上TCP协议防止SYN洪水的,TCP防止SYN洪水核心机制是SYN Cookie。这里就不详谈。

我们现在来说说三次握手的原因:

  1. 验证全双工--验证网络连通性
    仔细观测三次握手的过程:

    可以看到我们用了最小的通信次数,确保了通信双方都有接收和发送信息的能力。
  2. 建立双方通信的共识意愿
    一开始我就说了,建立连接要征得双方同意,三次握手就是用最小的通信次数得到了双方的通信意愿。

四次挥手

三次握手之后双方连接状态都是ESTABLISHED,进入正常通信。通信结束后要断开连接就要发起四次挥手,我们来看看四次挥手的过程:

可以看到四次挥手过程的连接状态变化比三次挥手要复杂,我们要详细理解每个状态的变化。

首先是close(fd)会发起四次挥手,即发送FIN报文:

那么我们要收到对方ack对不对?问题来了,我的文件描述符都关闭了怎么收到应答报文?

这里又要回到TCP怎么实现全双工了:

是的,TCP通信过程中有两个内核缓冲区。close(fd)之后首先只是关闭了发送缓冲区,此时发送缓冲区是只读的。那么对应的连接状态就是FIN_WAIT_1。

此时客户端只有两件事还要做:等待ACK和等待服务器FIN报文。

当收到ACK时,客户端就从FIN_WAIT_1变成FIN_WAIT_2,只需等待服务器FIN报文。

那么问题来了,为什么服务器发送ACK和FIN不用捎带应答机制?就是为什么不是三次挥手而是四次挥手?

这里要明确FIN的含义是:我的资源发送完毕,将来不会发送其他资源。意味着只是不发资源,不代表不接受资源。

关闭连接也是要双方同意的,服务器可能还想继续发送数据。

因此第二次挥手和第三次挥手之间,服务器还可以继续发送数据,此时服务器的连接状态时CLOSE_WAIT意味着,远端不会再发送资源。

这时服务器发送数据完毕,就会发送FIN报文,并且本身进入LAST_ACK状态,等待对方应答?

这里又有一个问题,服务器要是不主动调用close呢?

  • 此时客户端会不会阻塞无法关闭连接?

    答案是否定的,客户端会有其他手段强制进入TIME_WAIT状态,如

    shutdown强制关闭连接。

    或者在报头加上SO_KEEPALIVE选项。

    因此客户端一定会关闭连接。

  • 服务器会不会阻塞无法关闭连接?

    答案是会。如果服务器没有close,那么就会出现文件描述符泄漏等现象,也是一种严重的内存泄漏。

最后,服务器收到ACK就会关闭连接。

因此,如果问你为什么要四次挥手,那就是用最小的通信成本确保了双方的断开连接的意愿

注意客户端在收到第三次挥手时,连接状态没有进入CLOSED状态而是TIME_WAIT状态。

TIME_WAIT

TIME_WAIT-\>CLOSED\] 客户端要等待一个2MSL(MaxSegmentLife,报文 最大生存时间)的时间,才会进入CLOSED状态 为什么要有TIME_WAIT状态? 1. 就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的); 2. 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器会再重发一个FIN.这时虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK); 那么MSL是多长呢? MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Centos7上默认配置的值是60s; 我们可以通过cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e9605bfa2428415f96b36a923098e13d.png) TIME_WAIT状态有点过长也是不好的。因为当服务器重启后,我们的监听套接字状态是TIME_WAIT。这时候会拒绝绑定,也就是重启后会绑定失败。 如何避免这个情况呢? 我们可以通过: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/fe7fecf4b29e46d28b1e92e8e03eb4fe.png) 传入SO_REUSEADDR为1,来设置监听套接字的属性。 这样以后这个套接字就允许和TIME_WAIT的端口绑定。 ## 流量控制 我们TCP是个可靠的协议,他还需要确保双方的接受能力如何。就是发送方不能只管自己的发送能力,一股脑给接收方发送数据,接收方也有可能处理不了这么多数据。虽说我们可以重传,但是这样也是一种浪费资源的行为,应当尽量避免。 因此发送方需要知道接收方的的接收能力,我们这个接收能力有一个很直观的衡量就是接收缓冲区剩余大小。 因此,每次应答报文都会加上16位窗口大小: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c83c8b95e3c442b0890d8ce0683fe20a.png) 可以看到我们每次应答的时候都要带上窗口大小,这个窗口大小会动态变化。不管如何发送方发送的数据大小就不能超过窗口大小。 那么问题来了,我们发送方第一次传数据就随缘吗?那就太小瞧我们的TCP了,注意到我们发送数据前的三次握手了吗: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55114ea83dc243eb98aa2cb7e7ef901f.png) 我们在头两次握手的时候就已经告诉对方窗口大小了。 还有问题,当对方接收能力为0的时候,毫无疑问发送方要**阻塞发送** 。那么什么时候恢复发送呢? 这里有两套并行机制恢复发送: 1. 接收方发送窗口更新 接收方处理完缓冲区数据后,会发送一个包含非零窗口大小的ACK报文(称为窗口更新)。该ACK会触发发送方恢复传输。 2. 零窗口探测(Zero Window Probing) 发送方周期性发送探测报文(通常每5-60秒)。探测报文携带1字节数据,若接收方仍处于零窗口状态,会再次收到ACK=0的响应。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ee4fdee69db745a4a066ac2e2f2b7702.png) 要是接收方一直没有接收能力,发送方还能发送带有PSH(PUSH)标记位的报文: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b7eaecdb306245b3833c561c0e7f295d.png) 意味着让接收方应用层尽快处理缓冲区数据。 PSH还有别的应用场景。因为接收方未必是接收缓冲区有一丁点数据就读取,而是有一定的水位线,数据量要超过这个水位线才读取。所以PSH可以让他没超过水位线也读取,像xshell连接云服务器上的bash肯定就是每条指令带上PSH的。 那么问题来了,16位数字最大表示65535,那么TCP窗口最大就是65535字节么? 实际上,TCP首部40字节选项中还包含了一个**窗口扩大因子M**,实际窗口大小是窗口字段的值左移M位; 还有我们这个流量控制不能只减小发送速度,还要根据对方接收能力增大发送速度。 ## 紧急指针 紧急指针是配合URG(urgent,紧急)标记位一起使用的: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9b434d4456354b09b07b82b320954076.png) 如果标记位为0,就是没有启用,可以无需理会。 紧急指针里放的是一个偏移量,序号+偏移量对应的就是紧急指针存放的内容,这个内容只有一个字节,称为**带外数据(Out-of-Band Data)**,其他数据自然就是带内数据。 带外数据会优先读取处理。 这玩意有什么用呢? 要知道TCP是顺序传递数据的,如果我们在一些网站下载东西,想要取消的话也要顺序发送取消指令,那就太搞笑了。 因此就需要将取消指令放在带外数据里,立刻取消。 我们想发送带外数据可以在send和recv函数里的flags加上选项MSG_OOB: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/aae24d91cd8c420e995534086aa18e99.png) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/36fb15bb7f364404915b0f3f58f15b46.png) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1122b24681fe48cf88ac7e6dd5ed08cb.png) 事实上紧急指针的应用场景非常小,因为他指针传一个指针。大多数情况下,如果有紧急数据,我们会申请两个端口号,一个端口收正常数据,一个端口收紧急数据,这可比紧急指针好用多了。 至此,我们已经将TCP报头除了检验和部分都讲完了。 TCP检验和: > TCP检验和是一种用于检测TCP报文段在传输过程中是否发生错误的机制。计算TCP检验和的方法基于16位反码求和。 > > 计算伪首部时包含以下字段:源IP地址、目的IP地址、协议类型(6表示TCP)、TCP长度(TCP报文段长度)。将这些字段与TCP报文段一起参与检验和计算。 具体我们不做探讨。 剩下我们讲一下TCP的其他策略。 ## 滑动窗口 这个实现上实际上也和我们算法上的[滑动窗口](https://blog.csdn.net/Fy10030629/article/details/155168767?spm=1001.2014.3001.5502)非常像。 我们这里先提出两个问题: 1. 流量控制:在发送如何根据对方的接收能力,发送数据? 2. 超时重传:超时间以内,已经发送的报文不能丢弃而是要保存起来,保存在哪? 首先我们的发送缓冲区会维护一个滑动窗口: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2dcdba9df9de4bf0a1244f43212e7dd3.png) 我们把发送缓冲区的数据分成三个部分: 滑动窗口左侧:已发送已确认的数据,即废弃数据 滑动窗口:可以直接发送、暂时不用应答 滑动窗口右侧:未发送 既然滑动窗口的数据是可以直接发送的,那么一定要根据对方的接收能力来确定,我们暂时认为滑动窗口的大小就是Receive Window(接收窗口)的大小。 拥塞控制部分会纠正 那么我们考虑下面的发送信息情况: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ae05f6b0bfd14ffc9e1b8d5d4e6fa8de.png) 可以看到我们1001\~2000的数据丢失了,然后我们确认序号1001的含义是1001前的数都收到了。因此后续接收端只能发确认序号为1001,因为中间断了。而当发送端收到三次一样的确认序号应答时就会触发==高速重发控制(快重传)==机制,不管超时了没有,当他丢失了,直接重传数据。那么此时接收端前7000的数据都收到了,确认序号更新为7001.滑动窗口如此变化: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d3131ef101ee4ae3a6d2ee1f9f416ef4.png) 没错我们滑动窗口的起始地址要根据接收端的发送的确认序号更新,终点地址就是起始地址+窗口大小。 起始我们现在已经能回答开头的两个问题了 1. 流量控制:在发送如何根据对方的接收能力,发送数据? 在滑动窗口内的数据都能发送。根据滑动窗口进行流量控制。 2. 超时重传:超时间以内,已经发送的报文不能丢弃而是要保存起来,保存在哪? 存储在滑动窗口内。 滑动窗口的设计调高了我们的通信效率,允许我们并行发送数据。 此外我们再看一个场景: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/539fbb7f94a04d64a9998196c842a32a.png) 这里部分应答丢失了,还是根据我们的确定序号含义,后续更新窗口也是正常的。 这意味着允许我们丢失部分应答。 那么我们还有个担忧点,滑动窗口一直向右滑不会越界吗? 实际上我们的发送缓冲区是依据环形队列设计的,所以无需担心越界问题。 ## 拥塞控制 虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据.但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题. 因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵.在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的. TCP引入**慢启动**机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据; 我们这里引入一个拥塞窗口(Congestion Window, cwnd)的概念。 并且纠正滑动窗口大小=min(rcwnd,cwnd) * 发送开始的时候,定义拥塞窗口大小为1, * 每次收到一个ACK应答,拥塞窗口翻倍。 因此我们的慢启动策略确实启动慢,但是增长速度非常快。 为了不增长的那么快,因此不能使拥塞窗口单纯的加倍. 此处引入一个叫做慢启动的阈值 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c7219949ecef46eea1358c6a471655c1.png) 当TCP开始启动的时候,慢启动阈值等于窗口最大值; 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1; 少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为网络拥塞; 当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降; 拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案. 我们要明白一点:解决网络拥塞的问题,最大价值在于多个使用同一网络通信的主机,有拥塞避免意识。仅两台主机是无法解决问题的。 ## 延迟应答 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小. • 假设接收端缓冲区为1M.一次收到了500K的数据;如果立刻应答,返回的窗口就是500K; • 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了; • 在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来; • 如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是1M; 一定要记得,窗口越大,网络吞吐量就越大,传输效率就越高.我们的目标是在保证网络不拥塞的情况下尽量提高传输效率; 那么所有的包都可以延迟应答么?肯定也不是: * 数量限制:每隔N个包就应答一次 * 时间限制:超过最大延迟时间就应答一次 具体的数量和超时时间,依操作系统不同也有差异;一般N取2,超时时间取200ms; ## 面向字节流 我们又一次探讨面向字节流,和面向数据的整体发送数据不同。在TCP看来数据都是一堆字节无分首尾,因此如果发送的字节数太长,会被拆分成多个TCP的数据包发出,如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去。 此外TCP具有两个缓冲区: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/214bfea8056b46be9f5717da1d15e6a1.png) 由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如: 写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write, 每次写一个字节; 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read 100个字节, 也可以一次read一个字节,重复100次; 如此就会引发粘包问题 ### 粘包问题 在TCP的协议头中,没有如同UDP一样的"报文长度"这样的字段,但是有一个序号这样的字段. 站在传输层的角度,TCP是一个一个报文过来的.按照序号排好序放在缓冲区中. 站在应用层的角度,看到的只是一串连续的字节数据. 那么应用程序看到了这么一连串的字节数据,**就不知道从哪个部分开始到哪个部分**,是一个完整的应用层数据包 那么如何避免粘包问题呢?归根结底就是一句话,明确两个包之间的边界. 这其实就是应用的协议制定了,我们可以明确规定不同数据的边界如换行符。这个在[网络计算器](https://blog.csdn.net/Fy10030629/article/details/156194527?spm=1001.2014.3001.5502)部分已经实现过了。 这也告诉我们为什么前面[C++IO流](https://blog.csdn.net/Fy10030629/article/details/155076473?ops_request_misc=%257B%2522request%255Fid%2522%253A%25227deee8257f3572687f0c3ad244e4ca9b%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=7deee8257f3572687f0c3ad244e4ca9b&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-2-155076473-null-null.nonecase&utm_term=IO&spm=1018.2226.3001.4450)对于文件流的读写格式那么晦涩。因为当初我们没有相应的数据做序列化和反序列化所以就会出现粘包问题。 现在再去写文件管理,我们就知道要对数据进行序列化和反序列化。 ## TCP异常情况 进程终止:进程终止会释放文件描述符,仍然可以发送FIN.和正常关闭没有什么区别. 机器重启:和进程终止的情况相同. 机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会进行reset.即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在.如果对方不在,也会把连接释放. 实际上,TCP的保活定时器并不常用,可以说基本没用。因为他基本都是几十分钟往上才询问,和很多应用层不适配。 大多数情况下我们会在应用层实现类似的功能,比如我们可以自己设定应答时间间隔,如果没有应答就断开连接。 ## TCP小结 为什么TCP这么复杂?因为要保证可靠性,同时又尽可能的提高性能. 可靠性: • 校验和 • 序列号(按序到达) • 确认应答 • 超时重发 • 连接管理 • 流量控制 • 拥塞控制 提高性能: • 滑动窗口 • 快速重传 • 延迟应答 • 捎带应答 其他: • 定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等) ## 基于TCP应用层协议 • HTTP • HTTPS • SSH • Telnet • FTP • SMTP 还有自定义协议。 ## TCP/UDP对比 我们说了TCP是可靠连接,那么是不是TCP一定就优于UDP呢?TCP和UDP之间的优点和缺点,不能简单,绝对的进行比较 * TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景; * UDP用于对**高速传输和实时性**要求较高的通信领域,例如,早期的QQ,视频传输等.另外UDP可以用于广播 ## UDP实现可靠性 有时候我们需要一定可靠性,又不需要TCP这么可靠,我们就可以依照TCP的设计,在应用层方面改造UDP: * 引入序列号,保证数据顺序; * 引入确认应答,确保对端收到了数据; * 引入超时重传,如果隔一段时间没有应答,就重发数据;

相关推荐
鲨莎分不晴2 小时前
告别TCP?HTTP/3与QUIC协议如何重塑下一代Web体验
网络协议·tcp/ip·http
找不到、了2 小时前
HTTP 长连接 vs 短连接:从 TCP 优化到 QUIC 时代的演进
网络协议·tcp/ip·http
路由侠内网穿透.2 小时前
本地部署远程服务管理软件 IntelliSSH 并实现外部访问
运维·服务器·网络·网络协议
福尔摩斯张2 小时前
嵌入式硬件篇:常见单片机型号深度解析与技术选型指南
网络·数据库·stm32·单片机·网络协议·tcp/ip·mongodb
云老大TG:@yunlaoda3602 小时前
华为云国际站代理商GSL的跨境合规适配具体体现在哪些方面?
网络·数据库·华为云
安科瑞刘鸿鹏172 小时前
实时监测、主动预警:企业配电系统在线测温技术的场景化应用解读
运维·网络·人工智能·物联网
Linux运维技术栈2 小时前
靠这套内网DNS解决“IPv6优先级高+动态IPv6白名单配置繁琐+域名商IPv6不能关”的访问死局
网络·bind9
GCKJ_08242 小时前
【观成科技】银狐再进化:新型变种加密通信机制分析
运维·服务器·网络
Lueeee.2 小时前
RTMP协议
linux·网络