TCP协议-滑动窗口、拆包和粘包

本文深入解析TCP协议的关键特性,包括其可靠性、流控机制以及滑动窗口的工作原理。通过实例阐述了如何解决数据传输过程中的拆包与粘包问题。
TCP、UDP都可以完成从一端往另一端发送数据,
只是UDP只是负责从发送端将数据发送出去就完了,不再管数据是否发送到接收端是否已经接收到了;
而TCP不仅负责发送数据,还确保数据是否送达,TCP是可靠的,而且它也是可以流控的,管理发送的速度,不能超过设备的承受能力。

TCP特性


1.可靠性
Reliability,TCP提供数据传的可靠性,确保接收端是否已接收到数据,如果超时没有收到接收端的确认,则会重新传输,

2.流控的
Data Flow Control,TCP还提供数据传输的流控,有一个缓冲区接管,不能传输任意大的数据量,这就由滑动窗口来实现这个特性。

滑动窗口Sliding Window

在四层协议中,TCP的下一层就是IP层,IP层协议是不可靠协议,所以需要TCP层自己的确认机制去确认数据是否已经传输到接收端了,在较早时候,使用send-wait-send的模式,也叫stop-wait模式,发送端在发送数据包package1后,会启动定时器检查package1是否收到ACK,定时器到期后,还没收到ACK则认为接收端没收到package1,发送端就会重传,这样降低了通信的效率,这机制叫positive acknowledgment with retransmission (PAR)。

假设每发送一个包都有一个id,发送端对每个包进行ACK,那么发送端可以一次性发送多个包,而不必等待接收端ACK之后再发下一个,同时接收端也会告诉发送端它能接收多少,这样发送端就不会发送过多的包了,当然也要保证包的顺序性,接收端可以先缓存提前到的数据,如果超过一定时间,等待的数据还没到达,则丢掉之前缓存的数据。

引入滑动窗口解决这个问题。

先看发送端的滑动窗口:

1.Sent and Acknowledged
已发送并且已确认的,数据已经传输给接收端并收到ACK,这部分数据已经在窗口之外,因为他们已经传输成功了,会从窗口中移除出去,实际的操作是窗口进行合拢,将需要传输的数据放进窗口范围。

2.Send But Not Yet Acknowledged
已发送但未确认的,数据已发送到接收端,但还没收到接收端的ACK,这部分属于窗口之内,因为这部分数据认为是没完成发送的。

3.Not Sent,Recipient Ready to Receive
没发送的但接收端有能力接收的,这部分数据已经加载到缓存中,也是属于窗口之内的,其实这部分数据是完全在接收端的告知接受范围能力之内的,所以传输端要尽快传输给接收端。

4.Not Sent,Recipient Not Ready to Receive
没发送的并且接收端没能力接收的,超出了接收端的接收范围了,所以这部分数据是不属于窗口之内的。


发送窗口和可用窗口
所以对发送端来讲,属于可以发送的窗口之内的,就是2和3点了。2和3的范围又叫做发送窗口,发送窗口的范围是在TCP进行3次握手时,接收端告知发送端的,当然接收端在接收过程中也不断告知发送端发送窗口的大小: 3又叫可用窗口: 接收端告知这些数据也是在可接收范围之内的,但这些窗口范围还没发送数据出去的。


接收端的滑动窗口:

接收端不需要像发送端那样等待ACK,所以它没有确认部分的数据。
1.Received and ACK Not Send to Process
接收到发送端的数据并且给发送端发过ACK信息,但没被上层应用接收的,所以这部分数据也是在窗口之内的。

2.Received Not ACK
接收到发送端的数据但没给发送端ACK信息的,这部分属于Deplay ACK的,所以这部分也是属于窗口的。

3.Not Received
有空余的空间,还没有接收到发送端的数据。


滑动窗口原理
现在发送端有报文3、4要发送,接收端并不是每接收一个就回一个ACK,比如接收端先接收到4,但并不会马上对4回ACK,而在先放在缓存中等待3的空缺补上,如果3也送到了,再回一个ACK(又称累积ACK),如果3一直不送来,则会把已接收到的4也丢掉,这样发送端就执行重传机制。

1. 假设32~45 这些数据,是上层应用发送给TCP的,TCP将其分成四个报文段来发往接收端

2. seg1 32~34 seg3 35~36 seg3 37~41 seg4 42~45  这四个片段,依次发送出去,此时假设接收端之接收到了seg1 seg2 seg4

3. 此时接收端的行为是回复一个ACK包说明已经接收到了32~36的数据,并将seg4进行缓存(保证顺序,产生一个保存seg3 的空缺)

4. 发送端收到ACK之后,就会将32~36的数据包从发送并没有确认切到发送已经确认,踢出发送窗口,这个时候窗口向右移动

5. 假设接收端通告的Window Size仍然不变,此时窗口右移,产生一些新的空位,这些是接收端允许发送的范围

6. 对于丢失的seg3,如果超过一定时间,TCP就会重新传送(重传机制),重传成功会seg3 seg4一块被确认,不成功,seg4也将被丢弃

就是不断重复着上述的过程,随着窗口不断滑动,将整个数据流发送到接收端,实际上接收端的Window Size通告也是会变化的,接收端根据这个值来确定何时及发送多少数据,从对数据流进行流控。

我们可以通过机器的一些参数,看出TCP的发送、重传、连接失效机制。
cat /proc/sys/net/ipv4/tcp_mem
753600    1004800    1507200
cat /proc/sys/net/ipv4/tcp_wmem
4096    16384    4194304
cat /proc/sys/net/ipv4/tcp_rmem
4096    87380    4194304
cat /proc/sys/net/core/wmem_default
124928
cat /proc/sys/net/core/wmem_max
124928
cat /proc/sys/net/core/rmem_default
124928
cat /proc/sys/net/core/rmem_max
124928
cat /proc/sys/net/ipv4/tcp_retries2
15
cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
cat /proc/sys/net/ipv4/tcp_keepalive_probes
9
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
参照
http://www.360doc.com/content/14/0606/16/3300331_384326124.shtml


拆包和粘包

UCP是基于报文发送的,UDP报文的首部会有16bit来表现UDP数据的长度,所以不同的报文之间是可以区别隔离出来的,所以应用层接收传输层的报文时,不会存在拆包和粘包的问题;
而TCP是基于字节流传的,应用层和传输层之前数据交互是大小不等的数据块,但TCP对这些数据块只是一连串的数据流,它并不知道哪些数据块跟哪些数据块是该一起发,哪个数据块是应该单独一块的,因为TCP并没有像UDP那样首部有数据长度,所以TCP存在拆包和粘包的问题。

有了上面滑动窗口的知识,相信比较好理解为什么TCP会对数据块进行拆包和粘包的处理了,当然还有套接字(socket)缓存区可用大小的原因等等。

原因

1.要发送的数据包大于发送缓存区的可用空间时,数据被会拆包;
2.要发送的数据大于MSS(Maximum Segment Size最大报文段),TCP在发送前会对数据进行拆包;
TCP在三次握手建立连接过程中,会在SYN报文中使用MSS选项功能,协商交互双方能够接收的最大段长MSS值。
MSS是传输层TCP协议范畴内的概念,它是标识TCP能够承载的最大的应用数据段长度,因此,MSS=MTU-20字节TCP报头-20字节IP报头,那么在以太网环境下,MSS值一般就是1500-20-20=1460字节。
3.发送的数据小于缓存区,则TCP会将几次的数据一次性发送,会存在粘包;

拆包、粘包举例

比如发送端要往接收端发送2个数据包
1.收到2个数据包,没发送拆包和粘包情况;
2.收到1个数据包,TCP把2个数据包合成1个发送给接收端了,这样应用层不能处理合成1个的两个数据包,应用层不知道两个数据包之间的分隔在哪,所以很难处理,这是粘包问题;
3.收到2个数据包,但1个数据包产生了粘包(发送端的1个半数据包),另1个数据包产生了拆包(只有发送端中1个数据包的半个包),这样应用层也是很难处理粘包、拆包的;

解决

由于TCP是不知道应用层的数据包的分界的,所以我们应用层是决定不了传输层对数据包拆包还是粘包,不过应用层可以通应用层协议栈设计给数据包加分界标记,来处理最后接收到的数据,不管拆分还是粘包都可以处理好。

1.发送端给数据包增加首部,首部包含数据包中数据的长度,这样接收端的应用层接收数据后,根据首部中的长度就知道数据的实际长度了,可以很好处理数据了。
通常设计思路,比如第1个字段使用32int表示数据的长度,接着是数据内容。
2.设置数据包的长度为固定的长度,不够数据则以空格填补;
3.应用层在发送每个数据包时,给每个数据包加分界标记,比如回车换行,

在接收端接收的数据包个数总比发送端发送的个数少时,那是发送端的发送速度过快,导致接收端接收不过来,接收端会告知发送端的发送窗口为0,发送端新来数据只能暂存在发送缓存区中,当发送缓存区溢出后,则会出现丢包,可以增大发送缓存区来缓解,就是拥堵问题。
<think>好的,用户想了解CTCP协议中如何处理拆包、组的问题。首先,我需要确认CTCP协议的具体定义,但根据提供的引用,可能是指TCP协议中的相关问题,或者是某种定制化的TCP实现。由于引用中提到了Cisco Packet TracerTCP协议的分析,这里假设CTCP可能是指TCP,或者是用户笔误。 接下来,拆包、组TCP通信中常见的问题。拆包(Packet Splitting)指的是应用层的数据TCP分成多个报文传输;组(Packet Combining)是接收方将多个TCP报文重组为完整的数据(Sticky Packet)则是指接收方收到的数据连在一起,无法区分边界。 根据引用中的内容,引用[3]提到了应用层Packet的设计,可能需要继承Packet类并处理消息体的编码。这里可能涉及自定义头来标记数据长度,以解决问题。例如,固定长度的消息头(HEADER_LENGTH = 4)可能用于存储消息体的长度,接收方根据该长度进行拆包。 另外,引用[2]提到通过配置分析TCP连接来理解其过程,括连接建立、数据传输等。TCP本身是面向流的协议,不保留消息边界,因此应用层需要自己处理消息的分界。常见的方法括使用固定长度、分隔符或长度字段。 解决方案方面,需要结合应用层协议设计,比如在头添加长度信息,或者使用特定的分隔符。例如,在引用[3]的示例中,消息头长度是4字节,可能用来存储消息体的长度,这样接收方可以先读取头部的4字节,解析出长度后再读取对应长度的消息体,避免问题。 此外,用户可能需要具体的实现步骤或代码示例。根据引用中的代码结构,可以展示如何定义含长度头的Packet类,并实现拆包的逻辑。同时,需要注意缓冲区管理数据读取的完整性,防止数据不完整或多余的数据影响后续处理。 最后,生成相关问题时要围绕TCP协议处理、应用层设计、具体实现技术等方面,帮助用户深入理解解决方案。</think>关于CTCP协议拆包、组问题的解决方法,结合网络协议栈实现原理应用层设计,具体解决方案如下: ### 一、问题的本质 TCP作为面向字节流的传输层协议,不保留应用层报文边界[^2]。当应用层连续发送$N$字节$M$字节数据时,接收端可能收到$N+M$字节的连数据,这种现象称为。需要应用层协议自行定义报文边界。 ### 二、CTCP协议解决方案 #### 1. 固定长度头法(推荐方案) ```java // 引用[3]中Packet设计的改进实现 public class CTCPPacket extends Packet { // 固定4字节报文头存储body长度 public static final int HEADER_LENGTH = 4; private byte[] body; // 组时先写入body长度 public byte[] toBytes() { ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + body.length); buffer.putInt(body.length); buffer.put(body); return buffer.array(); } } ``` 此方法通过$$L_{header} = \lceil \log_{256}(MaxBodySize) \rceil$$计算头部长度,保证能表示最大报文长度[^3] #### 2. 特殊分隔符法 在报文尾部添加特定分隔符如`\r\n`,但需确保分隔符不出现在数据内容中: $$Packet = Body_{data} + Delimiter_{symbol}$$ #### 3. 动态缓冲区管理 接收端维护环形缓冲区实现拆包: ```python class BufferManager: def __init__(self): self.buffer = bytearray() def append(self, data): self.buffer.extend(data) def extract_packet(self): if len(self.buffer) >= 4: length = int.from_bytes(self.buffer[:4], 'big') if len(self.buffer) >= 4 + length: packet = self.buffer[4:4+length] self.buffer = self.buffer[4+length:] return packet return None ``` ### 三、性能优化要点 1. **滑动窗口机制**:通过TCP窗口大小动态调整传输速率,避免发送过快导致缓冲区溢出 2. **Nagle算法控制**:在实时性要求高的场景禁用该算法(设置`TCP_NODELAY`选项) 3. **MTU适配**:确保单个报文不超过网络最大传输单元,典型值为: $$MTU_{以太网} = 1500\ bytes$$ ### 四、异常处理机制 1. 超时重传:设置RTO(Retransmission Timeout)应对丢 2. 校验验证:通过CRC32或MD5校验报文完整性 3. 序号确认:采用SEQ/ACK序列号保证有序性
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值