TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在互联网协议族(Internet Protocol Suite)中,TCP层位于IP层之上,应用层之下,为应用层提供端到端的通信服务。TCP协议由IETF(互联网工程任务组,Internet Engineering Task Force)的RFC 793定义。
主要特点
面向连接:
- 在数据传输之前,TCP需要在两个通信端点之间建立一个逻辑连接。这个过程称为三次握手(three-way handshake),确保双方都准备好数据交换。
- 连接建立后,TCP会维护一个状态机,记录连接的状态,直到连接被优雅地关闭。
可靠性:
TCP确保所有发送的数据都能被接收方正确接收,通过以下机制实现:
- 序列号:TCP给每个传输的字节都分配一个序列号,确保数据按照正确的顺序到达。
- 确认应答:接收方收到数据后,会发送一个确认应答(ACK)给发送方,表示数据已经收到。
- 重传机制:如果发送方在指定的时间内没有收到确认应答,它会重传未被确认的数据。
- 数据校验:TCP头部包含一个校验和字段,用于检测数据在传输过程中的任何错误。
基于字节流:
- TCP将数据看作一个连续的字节流,而不是独立的报文。这意味着应用程序写入的数据可以以任何大小发送,而TCP协议负责将这些数据分割成合适大小的段进行传输。
- 接收方的TCP协议会重新组装这些段,确保应用程序读取到的数据与写入的数据顺序一致。
流量控制:
- TCP使用滑动窗口机制来控制发送方的数据流量,以匹配接收方的处理能力。
- 接收方通过发送窗口大小来告知发送方它愿意接收的数据量。发送方不会发送超过这个窗口大小的数据,以避免接收方的缓冲区溢出。
拥塞控制:
- TCP动态地监测网络拥塞的程度,并调整数据传输速率以避免进一步拥塞。
- 拥塞控制算法包括慢启动、拥塞避免、快速重传和快速恢复等,这些算法共同工作以保持网络的健康状态。
头部格式
我们首先来了解TCP的头部格式
其中重要的是以下4个部位
- 序列号(Sequence Number):32位,标识从源端向目的端发送的数据字节流中的第一个字节的序号。
- 确认号(Acknowledgment Number):32位,期待收到对方下一个报文段的第一个数据字节的序号。
- 数据偏移(Data Offset):4位,表示TCP头部的长度,以32位字(4字节)为单位,最大值为60字节。
- 标志位(Flags):共6位,每一位标志位具体含义如下:
- URG(Urgent):紧急指针有效。
- ACK(Acknowledgment):确认序号有效。
- PSH(Push):接收方应该尽快将这个报文段交给应用层。
- RST(Reset):重置连接。
- SYN(Synchronize):用于建立连接时的同步序号。
- FIN(Finish):表明此报文段的发送端的数据已经发送完毕,并要求释放连接。
TCP标识
在TCP/IP协议族中,一个TCP连接是由四元组(源IP地址,源端口号,目的IP地址,目的端口号)来唯一确定的。这四个值一起标识了网络中两个设备之间唯一的连接。
- 源IP地址:发送方的网络地址。
- 源端口号:发送方应用程序使用的端口号,通常由操作系统动态分配。
- 目的IP地址:接收方的网络地址。
- 目的端口号:接收方应用程序使用的端口号,通常是知名端口号或者是操作系统动态分配的。
连接建立
三次握手
在三次握手过程中,涉及到的报文类型主要有:SYN(同步序列编号)、ACK(确认应答)。
- 第一次握手(SYN):
- 客户端发送一个SYN报文到服务器,并进入
SYN_SENT
状态,等待服务器确认。 - 报文内容:标志位(SYN=1表示这是一个SYN报文),序列号(客户端选择的初始序列号
client_isn
)。
- 客户端发送一个SYN报文到服务器,并进入
- 第二次握手(SYN-ACK):
- 服务器收到客户端的SYN报文后,如果同意建立连接,则会发送一个SYN-ACK报文作为应答,并进入
SYN_RCVD
状态,等待客户端的确认。 - 报文内容:标志位(SYN=1表示这是一个SYN报文,ACK=1表示确认应答),确认号(
client_isn+1
,表示期望收到客户端的下一个报文段的序列号为client_isn+1
),序列号(服务器选择的初始序列号server_isn
)。
- 服务器收到客户端的SYN报文后,如果同意建立连接,则会发送一个SYN-ACK报文作为应答,并进入
- 第三次握手(ACK):
- 客户端收到服务器的SYN-ACK报文后,发送一个ACK报文作为确认,并进入
ESTABLISHED
状态。 - 报文内容:标志位(ACK=1表示这是一个确认报文),确认号(
server_isn+1
,表示期望收到服务器的下一个报文段的序列号为server_isn+1),序列号(client_isn+1
,表示客户端下一个报文段的序列号)。
- 客户端收到服务器的SYN-ACK报文后,发送一个ACK报文作为确认,并进入
服务器在收到这个ACK报文后,也进入ESTABLISHED
状态,此时,双方都准备好了,可以开始数据传输。
可以看到,客户端和服务端都收到了各自的一个SYN报文和ACK报文,其中SYN报文都是连接建立时第一次发送的报文。
why not两次握手
确保双方的接收和发送能力
三次握手可以确保双方的发送和接收能力都正常,而两次握手不行,因为两次握手只能证明单向的通信是正常的。让我们一步步来看:
- 第一次握手:客户端发送一个SYN报文到服务器,服务器能够接收到这个SYN报文,这证明了客户端的发送能力是正常的,同时服务器端的接收能力也是正常的。
- 第二次握手:服务器回复一个SYN-ACK报文给客户端,客户端能够接收到这个SYN-ACK报文,这证明了服务器端的发送能力是正常的,同时客户端的接收能力也是正常的。 到这里,两次握手已经完成了,它确实验证了客户端可以发送数据,服务器可以接收数据,服务器可以发送数据,客户端可以接收数据。但是,它没有验证客户端是否可以再次发送数据,以及服务器是否可以再次接收来自客户端的数据。
- 第三次握手:客户端发送一个ACK报文给服务器作为响应,服务器能够接收到这个ACK报文,这进一步确认了客户端的发送能力是正常的,并且服务器端的接收能力也是正常的。
通过第三次握手,我们确认了客户端在收到服务器的响应后,仍然可以发送数据,服务器也可以接收数据。这样,我们就验证了双向通信的完整性,即客户端和服务器都有能力发送和接收数据。
如果只有两次握手,那么即使客户端在第一次握手时发送了数据,服务器在第二次握手时也发送了数据,我们也没有确认客户端在收到服务器响应后是否还能发送数据,也没有确认服务器是否还能接收来自客户端的数据。这就存在一个潜在的问题,即客户端可能在发送完第一个SYN后失去发送能力,或者服务器可能在发送完SYN-ACK后失去接收能力,而两次握手无法检测到这种情况。因此,三次握手是必要的,以确保双方在整个通信过程中都具有发送和接收数据的能力。
确定初始化序列号
如果只有两次握手,客户端可以发送其序列号,服务器也可以发送其序列号并确认客户端的序列号。但是,客户端没有机会确认服务器发送的序列号。这意味着,如果服务器的序列号在传输过程中丢失,客户端将无法知道服务器的真实序列号,这将导致数据传输的混乱。
防止旧的连接请求建立连接
假设客户端发送了一个SYN报文到服务器以尝试建立连接。服务器收到这个SYN报文后,发送了一个SYN-ACK报文作为响应,准备建立连接。但是,由于网络问题,客户端没有收到这个SYN-ACK报文,因此客户端会重新发送一个SYN报文。
如果两次握手就足够了,服务器在收到第二个SYN报文后就会认为这是一个新的连接请求,并立即建立连接。然而,这个“新”的连接请求实际上可能是第一个连接请求的延迟重复。如果服务器为每个SYN报文都建立连接,那么它可能会为同一个客户端的多个SYN请求建立多个连接,这将导致资源的浪费,并可能带来安全风险,因为攻击者可以利用这个漏洞来发起拒绝服务攻击(DoS)。
在三次握手中,客户端在收到服务器的SYN-ACK响应后,会发送一个ACK报文作为确认。这个ACK报文包含了服务器序列号的确认,这样服务器就可以确认这个ACK是对当前连接请求的响应,而不是对旧的、延迟的SYN请求的响应。如果服务器收到一个没有对应的ACK的SYN报文,它就会知道这是一个旧的请求,并可以拒绝建立连接。
握手失败
第一次握手失败
如果TCP连接的第一次握手(客户端发送SYN报文到服务器)丢失了,会发生以下几件事情:
- 客户端等待响应:客户端发送SYN报文后,会进入
SYN_SENT
状态,并等待服务器的响应。客户端会根据超时重传计时器(通常称为TCP Retransmission Timer)来决定何时重传SYN报文。这个计时器通常是根据网络状况动态调整的。 - 服务器无响应:由于服务器没有收到客户端的SYN报文,它不会发送SYN-ACK响应,因此客户端不会收到任何响应。
- 重传尝试:客户端在等待一定时间后,如果还没有收到服务器的响应,它会重传SYN报文。重传的次数和间隔时间通常由TCP实现的具体参数决定。重传的报文与原始报文相同,保持原有的序列号。这是因为TCP使用序列号来确保数据的有序传输,接收方根据序列号来识别数据包的位置。
- 连接建立失败:如果重传次数耗尽,客户端仍然没有收到服务器的响应,它会最终放弃连接尝试,并认为连接建立失败。这时,客户端可能会通知应用程序连接失败,或者尝试重新发起连接。
- 服务器状态:由于服务器没有收到客户端的SYN报文,它不会为这个连接分配任何资源,也不会在TCP状态中记录任何关于这个连接的信息。
第二次握手失败
如果TCP连接的第二次握手(服务器发送SYN-ACK报文到客户端)丢失了,会发生以下几件事情:
- 客户端等待ACK:客户端在发送SYN报文后,会进入
SYN_SENT
状态,并等待服务器的SYN-ACK响应。客户端会根据超时重传计时器来决定何时重传SYN报文。 - 服务器等待客户端的ACK:服务器在发送SYN-ACK报文后,会进入
SYN_RCVD
状态,等待客户端的ACK响应。 - 客户端重传SYN:如果客户端在超时重传计时器到期前没有收到服务器的SYN-ACK响应,它会重传SYN报文,并重新启动计时器。
- 服务器可能重传SYN-ACK:服务器也可能有一个超时计时器,如果在服务器等待的时间内没有收到客户端的ACK,服务器可能会重传SYN-ACK报文。
- 连接建立失败:如果客户端和服务器重传次数耗尽,仍然没有成功交换ACK,双方会认为连接建立失败。客户端可能会通知应用程序连接失败,或者尝试重新发起连接。服务器则会从
SYN_RCVD
状态退出,释放相关资源(如端口号、缓冲区等)。
第三次握手失败
当TCP连接的第三次握手(客户端发送ACK报文到服务器)失败时,会发生以下事件:
- 客户端状态:客户端在接收到服务器的SYN-ACK响应后,会发送一个ACK报文,并进入
ESTABLISHED
状态。 - 服务器状态:服务器在发送SYN-ACK报文后,会进入
SYN_RCVD
状态,并等待客户端的ACK响应。 - ACK报文丢失:如果客户端发送的ACK报文在传输过程中丢失,服务器将不会接收到这个确认。
- 服务器重传SYN-ACK:由于服务器在等待客户端的ACK,它会启动一个超时重传计时器。如果在计时器到期前没有收到ACK,服务器会重传SYN-ACK报文,并重新启动计时器。
- 客户端发数据:客户端在发送了ACK后,如果认为连接已经建立,可能会开始发送数据。但由于服务器实际上并没有收到ACK,因此它不会认为连接已经建立,并会丢弃这些数据。
- 客户端收到重复的SYN-ACK:客户端可能会收到服务器重传的SYN-ACK报文。根据TCP协议,客户端应该响应一个ACK报文,即使这是一个重复的SYN-ACK,但这不是超时重传。
- 连接建立失败:客户端在等待服务器响应时,可能会因为超时而重新发送数据或终止连接尝试。
连接断开
四次挥手
TCP(传输控制协议)的“四次挥手”是用于终止一个TCP连接的过程。当通信双方完成数据传输后,需要通过这个步骤来确保双方的连接都能正确关闭。四次挥手的过程如下:
- 第一次挥手:客户端发送一个FIN(finish)报文给服务器,用以终止客户端到服务器方向的连接。客户端发送完这个报文后,进入
FIN_WAIT_1
状态,等待服务器的确认。 - 第二次挥手:服务器收到这个FIN报文后,发送一个ACK(acknowledge)报文作为应答,确认序号为收到的序号加1。服务器进入
CLOSE_WAIT
状态,客户端收到这个确认后进入FIN_WAIT_2
状态。此时,从客户端到服务器这个方向的连接就关闭了,但服务器到客户端方向的连接仍然开放,以便服务器发送剩余的数据。 - 第三次挥手:服务器发送完所有数据后,发送一个FIN报文给客户端,请求关闭服务器到客户端方向的连接。服务器发送完这个报文后,进入
LAST_ACK
状态。 - 第四次挥手:客户端收到这个FIN报文后,发送一个ACK报文作为应答,确认序号为收到的序号加1。然后客户端进入
TIME_WAIT
状态,而不是立即关闭。TIME_WAIT
状态持续的时间通常是2倍的最大段生命周期(Maximum Segment Lifetime, MSL),以确保足够的时间让对端的ACK能够到达。服务器收到这个ACK后,立即关闭连接。客户端在等待时间过后,也会关闭连接。
这个过程确保了TCP连接能够被双方优雅地关闭,防止了数据的丢失或错误。同时,TIME_WAIT状态的引入避免了已经关闭的连接在网络上延迟的数据段被错误地认为是新连接的数据段。
挥手失败
第一次挥手失败
如果TCP连接的第一次挥手(客户端发送的FIN报文)丢失了,客户端会进行重传。以下是详细的过程和可能的结果:
- 客户端重传FIN:客户端在发送FIN报文后进入
FIN_WAIT_1
状态,并等待服务器的ACK确认。如果在一定时间内没有收到确认,客户端会认为FIN报文丢失,并会重新发送FIN报文。 - 服务器响应:如果服务器收到了重传的FIN报文,它会像正常情况一样发送ACK确认,并进入
CLOSE_WAIT
状态。客户端收到ACK后进入FIN_WAIT_2
状态。 - 持续失败的情况:如果客户端重传的FIN报文持续丢失,客户端会继续重传,直到达到一定的重传次数上限。每种TCP实现都有自己的重传策略,通常包括指数退避算法,即在每次重传失败后,等待的时间会逐渐增加。
- 超时和连接终止:如果重传次数达到了上限,客户端会认为连接无法正常关闭,通常会触发超时,然后关闭连接。这可能导致客户端的TCP端口和资源被释放,而服务器端可能仍然认为连接是开放的,因为它没有收到FIN报文。
- 被动关闭:如果服务器在等待客户端数据时超时,它可能会主动发送一个FIN报文给客户端,以启动被动关闭连接的过程。这样,服务器就进入了
LAST_ACK
状态,等待客户端的ACK确认。 - 超时重传:如果服务器在发送数据后没有收到客户端的ACK确认,它会重传数据。如果重传次数达到上限,服务器会认为连接已经不可靠,可能会关闭连接。
第二次挥手失败
在TCP(传输控制协议)的四次挥手过程中,第二次挥手是服务器发送一个ACK确认报文,用于确认收到了客户端的FIN(结束)报文。如果这个ACK报文在传输过程中丢失,会发生以下情况:
- 客户端等待:客户端在发送FIN报文后进入
FIN_WAIT_1
状态,等待服务器的ACK确认。如果确认报文丢失,客户端会等待一个超时时间。 - 服务器等待:服务器在接收到客户端的FIN报文后进入
CLOSE_WAIT
状态,准备关闭连接并等待应用程序指示关闭连接。服务器会尝试发送ACK报文,但如果发送失败,它会等待一个超时时间并尝试重新发送。 - 重传尝试:客户端和服务器都会尝试重传它们各自的FIN和ACK报文,希望对方能够收到。
- 连接状态:由于双方都在尝试发送数据但失败,连接将保持在半开状态,客户端在
FIN_WAIT_1
状态,服务器在CLOSE_WAIT
状态。 - 资源占用:由于连接没有正常关闭,双方仍然会占用一些资源,如端口号和内存缓冲区。
- 超时和连接终止:如果重传尝试持续失败,最终双方的超时机制会生效。客户端可能会在多次尝试后放弃并关闭连接,释放资源。服务器如果在
CLOSE_WAIT
状态等待太久,也可能会超时并关闭连接。
第三次挥手失败
在TCP(传输控制协议)的四次挥手过程中,第三次挥手是指服务器在完成它的数据发送后,向客户端发送一个FIN(结束)报文,请求关闭从服务器到客户端的方向的数据传输。如果这个FIN报文在传输过程中丢失,会发生以下情况:
- 客户端未收到FIN:客户端在发送完它的FIN报文并收到服务器的ACK后,会进入
FIN_WAIT_2
状态,等待服务器的FIN报文。如果服务器的FIN报文丢失,客户端将不会收到这个结束请求。 - 服务器等待ACK:服务器在发送FIN报文后,会进入
LAST_ACK
状态,等待客户端的ACK确认。服务器预计客户端会回复一个ACK报文来确认结束请求。 - 服务器重试:服务器在等待客户端ACK确认的超时后,可能会重新发送FIN报文,尝试再次通知客户端它想要关闭连接。
- 连接最终关闭:只要客户端收到了服务器的FIN报文,它会回复一个ACK报文,然后进入
TIME_WAIT
状态。服务器收到这个ACK后,会关闭连接。客户端在经过2MSL(最大段生命周期)的时间后,也会关闭连接。 - 客户端可能超时:操作系统通常会有一个超时时间,如果客户端在
FIN_WAIT_2
状态停留的时间超过了这个时间,操作系统会自动关闭这个连接,释放资源。这个超时时间可以通过系统参数进行调整,如Linux中的tcp_fin_timeout
。
第四次挥手失败
在TCP(传输控制协议)的四次挥手过程中,第四次挥手是指客户端在收到服务器的FIN(结束)报文后,发送一个ACK(确认)报文作为响应。如果这个ACK报文在传输过程中丢失,会发生以下情况:
- 服务器未收到确认:服务器在发送FIN报文后,会进入
LAST_ACK
状态,等待客户端的ACK确认。如果客户端的ACK报文丢失,服务器将不会收到这个确认。 - 服务器重试:服务器在等待客户端ACK确认的超时后,可能会重新发送FIN报文,尝试再次通知客户端它想要关闭连接。
- 客户端可能重新发送ACK:客户端在发送ACK报文后,会进入
TIME_WAIT
状态,等待足够长的时间以确保服务器收到了它的确认。如果客户端在一段时间内没有收到服务器的任何响应,它可能会重新发送ACK报文,以确保服务器收到了它的确认。 - 连接最终关闭:只要服务器收到了客户端的ACK报文,它会关闭连接。客户端在经过ACK报文发送2MSL(最大段生命周期)的时间后,也会关闭连接。
- 资源清理:在四次挥手完成后,服务器和客户端都会清理与这个连接相关的资源,包括端口号和内存缓冲区等。
TCP和UDP的区别
TCP (Transmission Control Protocol) 和 UDP (User Datagram Protocol) 都是传输层协议,用于在网络中提供端到端的通信。它们的主要区别在于可靠性和效率方面。
TCP
- 可靠性:TCP 提供了一种可靠的、面向连接的服务。在数据传输之前需要建立一个连接(三次握手),并且在数据传输完成后会终止这个连接(四次挥手)。
- 数据确认:TCP 保证所有发送的数据都能按顺序到达接收方。它使用序列号和确认应答机制来确保数据包被正确接收。如果数据包丢失或损坏,TCP 会重新发送数据。
- 流量控制:TCP 使用滑动窗口机制进行流量控制,以避免发送方过快地发送数据导致接收方无法处理。
- 拥塞控制:TCP 还具有拥塞控制功能,可以动态调整发送速率以适应网络状况。
- 开销:由于上述特性,TCP 在每个数据包上增加了更多的头部信息,因此相比 UDP 具有更高的开销。
- 应用场景:适合对数据完整性要求较高的应用,如网页浏览(HTTP/HTTPS)、文件传输(FTP)、电子邮件等。
UDP
- 非可靠性:UDP 是一种无连接的服务,不需要事先建立连接就可以直接发送数据报文。
- 无序性:UDP 不保证数据的顺序传输,也不保证数据一定能送达接收方。
- 无重传机制:UDP 不会因为数据丢失或损坏而重新发送数据。
- 低开销:UDP 的头部开销小,只有8字节,因此它的处理速度通常比 TCP 快。
- 广播和多播支持:UDP 支持一对一、一对多、多对一和多对多的通信模式,非常适合广播或多播的应用场景。
- 应用场景:适用于对实时性要求较高但对数据完整性要求较低的应用,如在线视频、语音通话(VoIP)、多人游戏、DNS 查询等。
粘包和拆包
TCP是一个面向流的协议,数据以连续的字节流进行传输,没有明确的边界来区分不同的消息或数据包,因此容易出现粘包和拆包的问题。
具体来说,当发送方应用程序多次调用write操作而每次发送的数据量较小时,这些小数据包可能会在发送端被合并成一个大的数据包发送,从而发生粘包。相反,如果一个大数据包超过了TCP发送缓冲区的大小,它可能会被拆分成多个小包发送,导致拆包问题。
粘包
粘包是指发送方发送的多个数据包,在接收方接收时,多个数据包被合并成一个数据包的现象。粘包可能导致接收方无法正确解析每个独立的数据包,从而造成数据处理错误。
原因
- 应用层:如果发送方连续发送多个小的数据包,而TCP协议的优化机制可能会导致这些小包合并成一个大的数据包发送。
- TCP缓冲区:TCP维护一个发送缓冲区和接收缓冲区,数据在发送时可能不会立即发送出去,而是在缓冲区积累到一定量时才发送,这可能导致多个数据包合并。
- Nagle算法:这是TCP的一个默认选项,它旨在减少小数据包的发送,通过延迟发送,试图将多个小的数据包合并成一个大的数据包发送。
拆包
拆包是指一个数据包在传输过程中被分割成多个部分,在接收方需要将分割后的多个部分重新组装成一个完整数据包的现象。拆包要求接收方具备将分割的数据重新组装的能力,否则将无法恢复原始数据包,导致数据处理错误。
原因
- MTU(最大传输单元)限制:网络层对数据包的大小有限制,如果TCP层的数据包大小超过了这个限制,数据包就需要被分割成多个较小的包进行发送。
- 网络状况:网络状况不佳时,TCP可能会选择发送较小的数据包以增加传输的可靠性。
解决方案
为了解决TCP粘包和拆包的问题,通常有以下几种策略:
- 固定长度:每个数据包大小固定,不足的部分可以用空字节填充。
- 分隔符:在每个数据包的末尾添加特殊的分隔符来区分不同的数据包。
- 长度字段:在每个数据包的开始部分添加一个表示数据包长度的字段。
- 应用层协议:设计应用层协议,如HTTP、FTP等,它们有自己的方法来处理粘包和拆包问题。
固定长度
固定长度的策略要求每个数据包都有相同的长度。如果发送的数据不足这个长度,就需要在数据末尾填充空字节(通常是0)以达到固定长度。
实现方法:
- 确定一个合适的固定长度,这个长度需要足够大以容纳大多数数据包,但也不能过大,以避免过多的填充字节。
- 发送数据时,如果数据长度小于固定长度,则在数据后填充空字节。
- 接收数据时,直接按照固定长度读取数据,然后去除末尾的填充字节。
优点:
- 实现简单,处理速度快。
缺点:
- 如果数据通常远小于固定长度,会造成大量不必要的网络流量和存储空间的浪费。
- 如果数据偶尔大于固定长度,则需要特殊处理,如分割数据。
分隔符
分隔符策略是在每个数据包的末尾添加一个或多个特殊的字符(分隔符),用于区分不同的数据包。
实现方法:
- 选择一个或多个不会在数据内容中出现的字符作为分隔符。
- 发送数据时,在数据末尾添加分隔符。
- 接收数据时,读取数据直到遇到分隔符,然后截取分隔符之前的数据作为有效数据包。
优点:
- 不需要预先知道数据包的长度,处理灵活。
缺点:
- 如果数据内容中可能包含分隔符,需要对数据进行转义处理,增加了复杂性。
- 分隔符的识别和处理可能会消耗额外的计算资源。
长度字段
长度字段策略是在每个数据包的开始部分添加一个字段,该字段指示数据包的长度。
实现方法:
- 在数据包的开始部分添加一个固定长度的字段,用于存储数据包的长度信息。
- 发送数据时,先发送长度字段,然后发送实际的数据内容。
- 接收数据时,先读取长度字段,然后根据长度字段指示的长度读取数据内容。
优点:
- 可以处理任意长度的数据包,不需要填充或转义。
- 接收方可以准确地知道每个数据包的界限。
缺点:
- 需要额外处理长度字段,增加了数据包的大小。
- 如果长度字段本身损坏,可能会导致整个数据包解析失败。
应用层协议
使用成熟的应用层协议(如HTTP、FTP),它们已内置了数据包边界处理机制,有自己的方法来处理粘包和拆包问题。
HTTP解决方案
- 明确的消息边界:HTTP协议在传输时明确定义了请求行、请求头、响应行、响应头以及消息体(body)的结构,通过空行作为header与body的分界。
Content-Length
描述:HTTP的header中通常包含Content-Length
字段,该字段指示了消息体的长度,接收方依据此长度正确读取body数据。- Chunked传输编码:在不确定body长度的情况下,可以使用chunked传输方式,每个chunk包含长度和数据,最后一个长度为0的chunk表示body结束。
- 结束符:在某些情况下,HTTP协议还依赖于特定的字符来标识消息的结束,例如在HTTP/1.0中,如果
Content-Length
字段没有指定,则可以通过关闭连接来指示消息的结束。