传输层协议 TCP 介绍 -- TCP协议格式,确认应答机制,超时重传机制,连接管理机制,滑动窗口,流量控制,拥塞控制,延迟应答,捎带应答

目录

[1. TCP 协议格式](#1. TCP 协议格式)

[1.1 32 位序号(Sequence)和 32 位确认序号(Acknowledgment Number)](#1.1 32 位序号(Sequence)和 32 位确认序号(Acknowledgment Number))

[1.1.1 32 位序号](#1.1.1 32 位序号)

[1.1.2 32 位确认序号](#1.1.2 32 位确认序号)

[1.2 6 个标志位](#1.2 6 个标志位)

[1.3 16 位窗口大小](#1.3 16 位窗口大小)

[2. 确认应答机制](#2. 确认应答机制)

[2.1 单次数据传输的确认流程](#2.1 单次数据传输的确认流程)

[2.2 累积确认(Cumulative ACK)](#2.2 累积确认(Cumulative ACK))

[3. 超时重传机制](#3. 超时重传机制)

[4. 连接管理机制](#4. 连接管理机制)

[4.1 建立连接 -- 三次握手](#4.1 建立连接 -- 三次握手)

[4.2 释放连接 -- 四次挥手](#4.2 释放连接 -- 四次挥手)

[5. 滑动窗口](#5. 滑动窗口)

[6. 流量控制](#6. 流量控制)

[7. 拥塞控制](#7. 拥塞控制)

[8. 延迟应答](#8. 延迟应答)

[9. 捎带应答](#9. 捎带应答)

[10. 面向字节流](#10. 面向字节流)

[11. 粘包问题](#11. 粘包问题)

[12. TCP 异常情况](#12. TCP 异常情况)

[13. 基于 TCP 的应用层协议](#13. 基于 TCP 的应用层协议)


TCP 全称为 "传输控制协议(Transmission Control Protocol")。对数据的传输进行一个详细的控制。

TCP 是一个保证可靠性的前提下同时又尽可能的提高性能的协议。其中可靠性由校验和、序列化、应答机制、超时重传、连接管理、流量控制、拥塞控制机制进行保证,其中流量控制和拥塞控制是通过对其进行发送量的控制来减少丢包的概率保证可靠性的。而性能方面由滑动窗口、快速重传、延迟应答、捎带应答机制来进行保证的。

1. TCP 协议格式

下述为 TCP 协议的一个报文段。

(1)源端口号:表示数据是从哪个进程来,某 ip 地址的主机中的某一个进程。

(2)目的端口号:表示数据传送到目的 ip 指定的主机中的某个目的进程。

(3)4 位首部长度:表示该 TCP 报文段头部有多少个 4 字节的字段。 因为只有 4 位,所以最大表示为 15,其中前 20 字节是必须有的,所以 TCP 头部的大小范围为[20, 15*4 = 60]字节。

(4)16 位校验和:和 UDP 的 16 位校验和功能差不多,用于检测报文段在传输过程中是否发生比特错误(如位翻转),不做重点介绍。

(5)16 位紧急指针:用于指示报文中紧急数据的位置,使接收方能够快速识别并优先处理紧急数据。

(6)40 字节头部选项:暂时不做介绍。

1.1 32 位序号(Sequence)和 32 位确认序号(Acknowledgment Number)

在 TCP 中,32 位序号和 32 位确认序号是实现可靠数据传输的核心机制,用于解决数据传输中的顺序控制、重复检测、流量控制等。

1.1.1 32 位序号

序号用于标识 TCP 发送方发送的字节流中的每个字节,确保接收方能够按顺序重组数据,并检测重复或丢失的报文段。

TCP 将传输的整个字节流视为一个连续的序列(类似 char 类型的数组),每个字节分配一个唯一的序号(类似数组下标)。 例如:若发送方发送的数据为 ABCDE对应 ASCII 码(5 个字节),假设第一个字节 A 的序号为 100,则后续字节 BCDE 的序号依次为 101102103104

一个报文段可能包含多个字节,其首部的 32 位序号字段记录的是该报文段中第一个字节的序号。

初始序号(ISN,Initial Sequence Number):建立连接时(三次握手阶段),客户端和服务器会各自随机生成一个初始序号。

乱序重组:接收方根据序号将多个乱序到达的报文段重新排列成正确的字节流。

重复检测:若接收方收到序号重复的报文段,可判断为重复数据并丢弃。

流量控制与拥塞控制:序号帮助发送方跟踪已发送的数据,结合确认机制实现滑动窗口控制。

1.1.2 32 位确认序号

确认序号用于接收方向发送方确认已收到的数据,表示接收方期待的下一个字节的序号,并告诉发送方确认序号之前的数据都也成功接收。

确认逻辑:若接收方成功收到序号为 N 且长度为 L 的数据(数据的序号为 N ~ N + L - 1),则确认序号设置为 N + L,表示 N + L 之前的数据都已收到,并表示下一个期望接收到的报文段的序号为 N + L。

例如:发送方发送序号为 100、长度为 5 的报文段(数据字节 100~104)。接收方收到后,返回确认序号 105,表示 100~104 已接收,期望下次收到从 105 开始的数据。

确认序号与可靠性:发送方未收到确认的报文段会被视为丢失,并触发重传机制。

1.2 6 个标志位

主要关注 ACK,SYN,FIN 标志位。

(1)URG(Urgent,紧急标志位):表示报文中包含紧急数据,需要优先处理并表示 16 位紧急指针是否有效,紧急指针指向紧急数据的末尾位置。

(2)ACK(Acknowledgmen,确认标志位):表示报文段中 32 位确认序号有效。

发送方首次建立连接时(SYN报文段),ACK 标志位为 0(确认序号无效),接收方收到发送方的 SYN 报文段(SYN 标志位为 1)时,对其进行的应答报文段中 ACK 置为 1,并在确认序号中填入期望接收的下一个字节序号。

例如:若接收方成功收到序号为 100 的数据段(长度 5 字节),则应答数据段 ACK = 1且确认序号为 105。

(3)PSH(Push,推送标志位):提示接收方立即将数据从接收缓冲区交付给应用层,无需等待缓冲区填满。

(4)RST(Reset,重置标志位):用于重置连接,终止异常连接或处理错误。携带 RST 标识的称为复位报文段

(5)SYN(Synchronize,同步标志位):用于建立连接时的同步操作,请求初始化序号。携带 SYN 标识的称为同步报文段。

建立连接流程(三次握手):

a:客户端->服务端:SYN = 1,序号 = x(客户端初始序号),请求建立连接。

b:服务器->客户端:SYN = 1,ACK = 1,确认序号 = x + 1,序号 = y(服务端初始序号),确认连接请求。

c:客户端->服务端:ACK = 1,确认序号 = y + 1,序号 = x + 1,完成连接建立。

注意:SYN 数据段不携带数据,仅用于初始化连接参数。

(6)FIN(Finish,结束标志位):表示发送方已完成数据传输,请求释放连接。携带 FIN 标识的称为结束报文段。

连接释放流程(四次挥手):

a:主动关闭方->被动关闭方:FIN = 1,序号 = m,请求关闭连接。

b:被动关闭方->主动关闭方:ACK = 1,确认序号 = m + 1,序号 = n,确认收到关闭请求(此时连接处于半关闭状态,被动关闭方仍可发送剩余数据)。

c:被动关闭方->主动关闭方:FIN = 1,ACK = 1,确认序号 = m + 1,序号 = p,被动关闭方完成剩余数据传输后请求关闭。

d:主动关闭方->被动关闭方:ACK = 1,确认序号 = p + 1,序号 = m + 1,完成连接释放。

注意:FIN 数据段可携带数据,但实际应用中通常在发送 FIN 前完成数据传输。

1.3 16 位窗口大小

16 位窗口大小占 16 个bit 位(最大值为 216−1=65535 字节)。主要的功能是进行流量控制。

流量控制:接收方通过该字段告诉发送方当前接收方的接收缓冲区还能容纳多少字节的数据,发送方根据此字段调整发送窗口大小,避免发送过快导致接收方来不及处理

动态协商:窗口大小是一个动态变化的值,随接收方缓冲区的剩余情况实时调整,实现端到端的自适应流量控制。

2. 确认应答机制

2.1 单次数据传输的确认流程

发送方发送序号为 100、长度为 5 的数据(字节 100~104)。接收方收到数据后,返回 ACK = 1 报文,确认号设为 105(表示前 5 字节已接收,期望下一个字节从 105 开始)。发送方收到 ACK 后,确认数据传输成功,继续发送后续数据。

2.2 累积确认(Cumulative ACK)

定义:接收方不必对每个数据段单独确认,而是可以确认最后一个按序到达的字节,表示该字节之前的所有数据均已正确接收。

这里给出一个前提,发送方不一定要收到上一个报文段的应答之后才发送第二个报文段,而是可以暂时不用收到应答发送多个报文段。 发送方依次发送数据段 1(序号 100,长度 5)、数据段 2(序号 105,长度 5)、数据段 3(序号 110,长度 5)。若接收方仅收到数据段 1 和 3(数据段 2 丢失),则返回的确认号仍为 105 (即使序号 110~114 的数据已到达,但是只能确定序号 105 之前的数据被完全接收)。发送方收到确认号 105 后,会重传序号 105 开始的数据(即数据段 2)。

3. 超时重传机制

主机 A 发送数据给主机 B 之后,可能因为网络拥堵等原因,数据无法到达主句 B,如果主机A 在一个特定的时间间隔内没收到 B 发来的确认应答,就会触发超时重传机制,重新发送数据。

但是如下图,如果主机 A 发送的数据主机 B 收到了,但是主机 B 的应答丢失了,这个时候触发重传机制,主机 B 会收到重读的数据,这时候可以利用序号就可以对重复数据进行去重。

如下图,还有一种情况就是发送方依次发送多个暂时不需要应答的报文段, 其中如果 1001 - 2000 的数据丢失了,其他的全部都接收到了,这时候确认序号还是 1001。如果 1001 - 2000 的数据没有丢失,只是这个数据段的应答丢失了,则主机 B 在接收到 1001 - 2000 的时候就更新了确认序号,后续数据正常按序接收,确认序号就会依次更新,就算 1001 - 2000 数据段的应答丢失了也不会触发超时重传机制。

特定的时间间隔最理想的情况下是找到一个最小的时间,保证确认应答一定能在这个时间内返回。但是这个时间的长短,随着网络环境的不同是有所差异的,如果间隔时间太长,会影响整体的重传效率,如果间隔时间太短,可能会频繁发送重复的数据段。

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

Linux 中(BSD Unix 和 Windows 也是如此),超时以 500 ms 为一个单位进行控制,每次判定超时重发的超时时间都是 500 ms 的整数倍。第一次等待 500 ms 没有收到应答,则重传一次,这次等待 500 * 2 ms,若过了 1000 ms 之后还是没有收到应答,则进行第二次重传,这次等待时间为 500 * 2 * 2 ms,后面以此类推,以指数形式递增

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

4. 连接管理机制

在正常情况下,TCP 要经过建立连接(三次握手)和释放连接(四次挥手)的过程。

4.1 建立连接 -- 三次握手

TCP 建立连接的过程称为三次握手,其核心是通过客户端和服务端之间的三次交互,确认双方的通信能力并同步初始序列号。具体过程如下:

(1)第一次握手:客户端向服务器发送一个 SYN 标志位为 1 的 TCP 报文段 (只有报头)。报文段中包含发送方的初始序列号m,用于标识后续数据的起始位置,不包含数据载荷。此时,客户端的状态从 CLOSED 变为 SYN_SENT 状态

(2)第二次握手:服务器收到 SYN 报文段之后,确认连接请求,应答一个 SYN + ACK 标志位均为 1 的报文段 。报文中包含确认序号m+1,以及服务器的初始序号n,不包含数据载荷。此时,服务器从 LISTEN 状态变为 SYN_RCVD 状态

(3)第三次握手:客户端返回 ACK 报文段,发送一个 ACK 标志位为 1 的报文段 。报文段中包含确认序号n+1,序号m+1,可包含数据载荷。客户端从 SYN_SENT 状态变为 ESTABLISHED 状态,服务器从 SYN_RCVD 状态变为 ESTABLISHED 状态 。其中,客户端发出应答报文之后就为 ESTABLISHED 状态,而服务器收到应答之后才变为 ESTABLISHED 状态。

知识点1:

两次握手是否可行?不可行,如果只是两次握手,则服务器没有收到客户端的应答,服务器无法确认客户端是否收到 SYN+ACK 报文段。若客户端未收到 SYN+ACK (如丢包),服务器会一直等待数据,导致资源浪费(半连接状态)。

知识点2:
三次握手中的 SYN 报文段会消耗资源吗?会,服务器收到 SYN 报文段后进程 SYN_RCVD 状态,并分配内存等资源。

4.2 释放连接 -- 四次挥手

TCP 释放连接的过程称为四次挥手,用于确保客户端和服务器双方都能安全、可靠的终止数据传输。核心时让双方确认数据已传输完毕并释放资源

(1)第一次挥手:主动关闭方发送一个 FIN 标志位为 1 的 TCP 报文段。 报文段中包含序列号m,表示"已完成数据发送,请求关闭连接"。该报文段可能携带数据,FIN 标志位表示后续不再有新数据发送主动关闭方从 ESTABLISHED 状态变为 FIN_WAIT_1 状态

(2)第二次挥手:被动关闭方收到 FIN 报文段之后,返回一个 ACK 标志位为 1 的报文段 。其中确认序号为m+1,表示"我已收到你的关闭请求"。此时,被动关闭方仍可以继续发送数据(此时处于半关闭状态,被动关闭方仍然可以向主动关闭方发送数据)被动关闭方从 ESTABLISHED 状态变为 CLOSE_WAIT 状态,主动关闭方收到 ACK 报文段之后从 FIN_WAIT_1 状态变为 FIN_WAIT_2 状态,

(3)第三次挥手:被动关闭方完成所有数据的发送之后,发送 FIN 标志位为 1 的报文段 。报文段中的包含被动关闭方的序号n,表示"我已完成数据发送,请求关闭连接"。被动关闭方从 CLOSE_WAIT 状态变为 LAST_ACK 状态

(4)第四次挥手:主动关闭方收到 FIN 报文段之后,返回 ACK 标志位为 1 的报文段 。报文中的确认序号为 n+1,表示已收到关闭请求。主动关闭方启动超时定时器,确保最后一个 ACK 报文段到达被动关闭方。主动关闭方从 FIN_WAIT_2 状态变为 TIME_WAIT 状态,等待 2MSL 后变为 CLOSED 状态。被动关闭方收到 ACK 报文段之后,从 LAST_ACK 状态变为 CLOSED 状态

知识点1:
为什么挥手需要四次?三次握手时 SYN+ACK 可合并,挥手时 FIN 和 ACK 需要分开,因为服务器收到 FIN 之后,可能仍有数据未发送完,需要先回复 ACK 确认关闭请求,等待数据发送完后再发送 FIN。

MSL(Maximum Segment Lifetime):报文在网路中的最大生存时间 。 可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 命令查看 MSL 的值。一般为 60 s

TCP 协议规定,主动关闭连接的一方要处于 TIME_WAIT 状态,等待 2MSL 的时间后才能回到 CLOSED 状态 。MSL 是 TCP 报文的最大生存时间,因此 TIME_WAIT 持续存在 2MSL 的话,就能保证在两个传输方向上的尚未被接收的报文段都已经消失了。

知识点2:

为什么在 TIME_WAIT 状态停留 2MSL?(1)防止最后一个 ACK 丢失:若被动关闭方未收到 ACK,会重传 FIN,此时主动关闭方在 TIME_WAIT 状态可重新响应。(2)避免旧连接报文干扰:确保网络中所有旧连接的报文段过期,防止新连接(相同端口)收到旧数据。

所以之前主动将服务器的服务关闭,立即重新启动绑定同一个端口号,bind 函数会绑定出错,因为上次的连接还没有结束,处于 TIME_WAIT 状态。

知识点3:

如何解决处于 TIME_WAIT 状态导致的端口占用?高并发服务器短时间内关闭大量连接,TIME_WAIT 状态会占用端口和资源。设置 SO_REUSEADDR 套接字选项,允许端口重用(可能接收旧连接报文)。

对于 CLOSE_WAIT 状态的理解:当服务器中的代码在关闭连接的时候没有使用 close() 函数关闭 socket。此时客户端关闭,服务器还占用该文件描述符。

如果未正确处理 CLOSE_WAIT 状态,而可能导致客户端长时间等待,服务器累积大量版关闭连接,占用内存和文件描述符

下图客户端和服务器通过 TCP 协议进行通信时,从 建立连接->数据传输->释放连接 的整个过程的示意图:

5. 滑动窗口

前面的确认应答机制,对每一个发送的数据段,都要给一个 ACK 确认应答。收到 ACK 后再发送下一个数据段。这样做有一个比较大的缺点就是性能较差。如下图:

那么一次发送多条数据,就可以大大的提升性能(将多个段的等待时间重叠在一起)。

滑动窗口(Sliding Window)机制是实现高效可靠传输的核心算法之一,它通过动态调整发送窗口大小,平衡了可靠性与吞吐量。

**窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。**上图的窗口大小为 4000个字节(四个段)。发送前四个段的时候,不需要等待任何 ACK,直接发送。收到第一个 ACK 后,滑动窗口向后移动,继续发送第五个段的数据,以此类推。窗口越大,则网络的吞吐率就越高。

操作系统内核为了维护这个滑动窗口, 需要开辟发送缓冲区来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉。

这里对滑动窗口进行以下理解:将发送端的发送缓冲区看作一个数组,数组下标的最大值就是发送缓冲区的大小,然后滑动窗口就是发送缓冲区上的一段子数组,滑动窗口左边的下标表示该下标之前的数据都已发送并确认,滑动窗口右边的下标表示该下标之后的数据未发送,两个下标中间的数据表示已发送但未确认的数据。实际过程中可以把

滑动窗口左下标的值就是最新的一个应答中的确认序号 ,在最新一个应答中,会返回一个 16位窗口大小的字段,该字段表示下一个滑动窗口的范围是 [确认序号,确认序号 + 窗口大小]。所以滑动窗口的大小是由接收方给的应答数据段中的 16 位窗口大小来确认的。

如果出现了丢包,如果进行重传呢?这里分两种情况讨论。

(1)情况1:数据包已经到达,ACK 丢失了

这种情况下,部分 ACK 丢了没有关系,因为当主机 B 收到数据的时候,确认序号就已经更新了,所以后续都会按序更新确认序号,不会进行重传。

(2)情况2:数据报丢失

如上图,当 1 - 1000 的数据被接收之后,确认序号会更新到 1001,但是当 1001 - 2000 的数据丢失之后,确认序号不会进行更新,再收到后续的数据时,返回的确认序号都是 1001。 如果主机 A 连续收到三个 1001 这样的应答,就会将对应的数据 1001 - 2000 重新发送。这时候主机 B 收到 1001 - 2000 的数据之后,再次返回的 ACK 就是 7001,因为后续数据已经收到了,被放到了接收到操作系统内核的接收缓冲区中,当 1001 - 2000 数据收到时,确认序号被更新为 7001。这种机制被称作快速重传。

6. 流量控制

**接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,这时候如果发送端继续发送,就会造成丢包,继而引起丢包重传。**虽然丢包引起重传是没有错的,但是如果接收端上层一直不拿去接收缓冲区的数据,一直重传数据就会导致对网络、电力资源的浪费。

因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制(Flow Control)

流量控制的原理接收端将自己的接收能力放入 TCP 数据段首部中的 16 位窗口大小字段中,通过 ACK 通知发送端;接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值发送端接收到这个 ACK 数据段后,会根据其中的 16 位窗口大小减少发送的数据 ,这样就可以减慢发送速度了;如果接收端缓冲区满了,就会将窗口设置为 0,这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,当接收端缓冲区大小改变的时候,这时便于接收端把窗口大小告诉发送端。

16 位最大表示 65535,那么 TCP 窗口最大就只有 65535 字节吗?实际上,TCP 报头中 40 字节选项中还包含一个窗口扩大因子 M,实际窗口的大小是窗口字段的值左移 M 位

7. 拥塞控制

虽然滑动窗口能够高效可靠的发送大量数据,但是在不清楚当前网络情况的状态下,刚开始就发送大量的数据,仍然可能引发问题。

因为网络上有很多计算机,可能当前的网络状态就已经比较拥堵了。在不清楚当前网络状态下,发送大量数据很有可能使其网络更加的拥堵。

TCP 的拥塞控制是解决网络中数据传输过载问题的核心机制,它通过动态调整发送端的流量速率,避免网络拥塞并优化资源利用率

拥塞控制基于四个经典算法的协同工作,形成 "探测 - 适应 - 反馈" 闭环。

(1)慢启动:初始阶段逐步探测网络带宽,避免突发流量导致拥塞。

初始拥塞窗口(cwnd)设为 1 - 2 MSS (最大段大小,通常约 1460 字节)。每收到一个 ACK ,cwnd 按加法增加,(收到 n 个 ACK,cwnd 增加 n 个 MSS),这个增加其实是呈指数增加的,因为每个 RTT (往返时间)内 ACK 数量随发送量增加。

比如设置初始 cwnd = 1 MSS。第一个 RTT 中收到一个 ACK,此时 cwnd = 2 MSS;第二个 RTT 中就会发送两个数据段,收到两个 ACK,此时 cwnd = 4 MSS;第三个 RTT 中就会发送四个数据段,此时收到四个 ACK,此时 cwnd = 8 MSS,依次类推。

其中有个阈值(ssthresh),当 cwnd 超过 ssthresh 时,切换至拥塞避免阶段,初始 ssthresh 通常为 65535 字节,可调整。

(2)拥塞避免:在接近网络容量时,以线性方式增加发送速率,防止拥塞

每经过一个 RTT,cwnd 增加一个 MSS,若检测到丢包(3 个重复 ACK),触发快速重传和快速恢复

(3)快速重传:在丢包时快速重传,减少等待超时的时间,提升吞吐量

发送方收到 3 个重复 ACK 时,判定中间包丢失,立即重传,无需等待超时。

(4)快速恢复:快速重传后,避免直接回到慢启动,维持部分已探测的带宽

触发快速重传时,将 ssthresh 设置为当前 cwnd 的一半,cwnd 设为 ssthresh + 3 MSS,进入拥塞避免阶段,以线性方式增加 cwnd。慢启动在丢包后从 cwnd = 1 MSS 开始,而快速恢复保留部分窗口大小,减少带宽浪费。

上图为拥塞控制时阶段的转变。

在流量控制中,流量的控制是由滑动窗口和拥塞控制一起决定的,在发送的时候,发送数量的多少取滑动窗口和拥塞窗口的最小值

8. 延迟应答

如果接收端在接收到数据就立即返回 ACK 应答,则此时由于上层还没有对接收缓冲区的数据进行读取,返回的窗口可能较小。延迟应答就是在接收到数据之后不是立即返回 ACK 应答,而是稍微等一会再进行应答,在等的这段时间,接收端上层就会从接收缓冲区读取数据,接收缓冲区就会变大,然后返回一个比较大的窗口

并不是每一个包都进行延迟应答。可以从两个维度对其进行限制:(1)数量限制:每隔 N 个包就应答一次;(2)时间显示:超过最大延迟时间应答一次。具体的数量和最大延迟时间在不同的操作系统上是有差异的,一般 N 取 2,最大延时时间取 200 ms。

9. 捎带应答

一般情况下在发送方发送数据之后,接收方只需要进行应答,而接收方在进行发送的时候,发送方也只需要进行应答。当接收方进行应答的时候同时也需要向发送方发送数据时,这样就可以在同一个数据段中即有 ACK 应答,也有有效数据载荷。

10. 面向字节流

创建 TCP socket 的同时会在内核中创建一个发送缓冲区 和一个接收缓冲区

发送方调用 write 时,数据先写入到发送缓冲区中,如果字节太长了,会被拆分成多个 TCP 数据段发出,如果发送的字节数太短,就会先在发送缓冲区中等待,等缓冲区长度差不多了或其他合适的时机再进行发送。

接收方接收数据时,数据从网卡驱动程序到达内核的接收缓冲区,然后上层应用调用 read 从接收缓冲区中拿数据。

由于缓冲区的存在,TCP的读写次数不需要相同。写入 100 个字节,可以调用一次 write 写入 100 字节,也可以调用 100 次 write,每次写入一个字节。

读 100 个字节数据时,也可以一次读 100 字节,也可以调用 100 次 read,每次读一个字节。

发送缓冲区的数据发送到网络中,发送次数和发送数量又 TCP 协议自己控制,将数据从网卡驱动程序中读入接收缓冲区的次数和数量也是由 TCP 协议自己控制

11. 粘包问题

首先明确,粘包问题中的"包"指的是应用层的一个数据包。在 TCP 协议中没有 UDP 协议一样的"报文长度"字段,但是有一个序号字段。

在传输层的角度,TCP 接收缓冲区的数据是一个一个报文中的数据按序号排好放在里面的。而应用层看到的只是一串连续的字节数据。

这个时候就需要在应用层定义协议来规定每次从 TCP 接收缓冲区中取多少字节为一个数据包。

对于定长的包,每次都按固定大小读取即可。

对于变长的包,可以在数据包包头(应用层协议的报头)约定该数据包的长度,从而知道包的结束位置。

12. TCP 异常情况

(1)进程终止:进程终止会释放文件描述符,在释放的过程中仍然可以发送 FIN,和正常关闭没有区别。

(2)机器正常关闭或重启:在正常关闭机器或重启的的过程在,操作系统会先对机器中的进程进行终止,所以情况同上。

(3)机器掉电(直接拔掉电源线)或网线断开:当客户端发送这种情况的时候,是可以识别当网络发送了问题,客户端断开和服务端的连接,但是没有机会和服务端进行正常的四次挥手,所以服务端会认为客户端还是好的,只是一直没有收到应答。服务端发现对方连接不活跃,但是服务器一旦有写入操作,就会像客户端发送 reset,请求重新建立连接,其次如果没有写入操作,TCP 也内置了一个保活定时器,定期询问对方是否还在。超过一定时间没有应答则会进行连接的释放。

13. 基于 TCP 的应用层协议

HTTP:用于万维网数据传输,每次请求独立,需依赖 Cookie/Session 维持会话。使用 80 号端口。

HTTPS:用于安全的 Web 通信,例如支付、登录、敏感数据的传输等,使用 443 号端口。

SSH:用于远程登录、安全命令执行、端口转发,替代不安全的 Telnet 协议,使用 22 号端口。

Telnet:用于远程终端连接,使用 23 号端口。

相关推荐
小疆智控1 小时前
从离散控制到集成管理:Modbus TCP转CANopen网关重构烟丝膨胀生产线
网络协议·tcp/ip·重构
国际云,接待1 小时前
微软云注册被阻止怎么解决?
服务器·网络·microsoft·云原生·微软·云计算
创小匠2 小时前
创客匠人:AI重构知识IP定位与变现效率新范式
人工智能·tcp/ip·重构
laocooon5238578862 小时前
基于Python的TCP应用案例,包含**服务器端**和**客户端**的完整代码
网络·python·tcp/ip
Blossom.1184 小时前
基于区块链的去中心化身份验证系统:原理、实现与应用
运维·服务器·网络·人工智能·机器学习·去中心化·区块链
沐森4 小时前
基于Fetch的post sse实现
网络协议
SZ1701102314 小时前
HTTP 请求报文 方法
网络·网络协议·http
Bruce_Liuxiaowei4 小时前
使用Nmap探测VNC服务信息—某单位KVM设备
网络·安全·web安全
比奥利奥还傲.4 小时前
QNAP威联通NAS配置SFTP与内网穿透技术实现远程文件访问
网络
君鼎5 小时前
C++操作系统与网络编程(针对特定岗位)
网络·c++