TCP实现顺序传输的原理

转自:http://blog.csdn.net/ggxxkkll/article/details/7894112

我和大家一起讨论下TCP在保证可靠传输数据的前提下,是怎样对传输的数据进行顺序化操作的。
大家都知道,TCP提供了最可靠的数据传输,它给发送的每个数据包做顺序化(这看起来非常烦琐),然而,如果TCP没有这样烦琐的操作,那么,可能会造成更多的麻烦。如造成数据包的重传、顺序的颠倒甚至造成数据包的丢失。
那么,TCP具体是通过怎样的方式来保证数据的顺序化传输呢?

1. 主机每次发送数据时,TCP就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认,

2. 如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。

3. 接收主机利用序列号对接收的数据进行确认,以便检测对方发送的数据是否有丢失或者乱序等,

4. 接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。


具体步骤如下:

(1)为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区; 

(2)并为每个已发送的数据包启动一个超时定时器; 

(3)如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区; 

(4)否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。

(5)接收方收到数据包后,先进行CRC校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。

==============================================================================================

以下转自:http://www.cnblogs.com/lidabo/p/4245429.html
<p>a)  由于TCP要通过协商解决发送出去的报文段的长度,因此我们发送的数据很有可能被分割甚至被分割后再重组交给网络层发送,而网络层又是采用分组传送,即网络层数据报到达目标的顺序完全无法预测,那么收包会出现半包、粘包问题。举个例子,发送端连续发送两端数据msg1和msg2,那么发送端[传输层]可能会出现以下情况:</p><p>                i.    Msg1和msg2小于TCP的MSS,两个包按照先后顺序被发出,没有被分割和重组</p><p>               ii.    Msg1过大被分割成两段TCP报文msg1-1、msg2-2进行传送,msg2较小直接被封装成一个报文传送</p><p>               iii.   Msg1过大被分割成两段TCP报文msg1-1、msg2-2,msg1-1先被传送,剩下的msg1-2和msg2[较小]被组合成一个报文传送</p><p>               iv.   Msg1过大被分割成两段TCP报文msg1-1、msg2-2,msg1-1先被传送,剩下的msg1-2和msg2[较小]组合起来还是太小,组合的内容在和后面再发送的msg3的前部分数据组合起来发送</p><p>               v.     ……………………….太多……………………..</p><p>b)  接收端[传输层]可能出现的情况</p><p>               i.     先收到msg1,再收到msg2,这种方式太顺利了。</p><p>              ii.     先收到msg1-1,再收到msg1-2,再收到msg2</p><p>             iii.     先收到msg1,再收到msg2-1,再收到msg2-2</p><p>             iv.     先收到msg1和msg2-1,再收到msg2-2</p><p>              v.     …………还有很多………………</p><p>c)  其实“接收端网络层”接收到的分组数据报顺序和发送端比较可能完全是乱的,比如发“送端网络层”发送1、2、3、4、5,而接收端网络层接收到的数据报顺序却可能是2、1、5、4、3,但是“接收端的传输层”会保证链接的有序性和可靠性,“接收端的传输层”会对“接收端网络层”收到的顺序紊乱的数据报重组成有序的报文[即发送方传输层发出的顺序],然后交给“接收端应用层”使用,所以“接收端传输层”总是能够保证数据包的有序性,“接收端应用层”[我们编写的socket程序]不用担心接收到的数据的顺序问题。</p><p>d)  但是如上所述,<span style="color:#FF0000;">粘包问题和半包问题不可避免</span>。我们在接收端应用层需要自己编码处理粘包和半包问题。一般做法是定义一个缓冲区或者是使用标准库/框架提供的容器循环存放接收到数据,边接收变判断缓冲区数据是否满足包头大小,如果满足包头大小再判断缓冲区剩下数据是否满足包体大小,如果满足则提取。详细步骤如下:</p><p>          1.  接收数据存入缓冲区尾部</p><p>          2.  缓冲区数据满足包头大小否</p><p>          3.  缓冲区数据不满足包头大小,回到第1步;缓冲区数据满足包头大小则取出包头,接着判断缓冲区剩余数据满足包头中定义的包体大小否,不满足则回到第1步。</p><p>          4.  缓冲区数据满足一个包头大小和一个包体大小之和,则取出包头和包体进行使用,此处使用可以采用拷贝方式转移缓冲区数据到另外一个地方,也可以为了节省内存直接采取调用回调函数的方式完成数据使用。</p><p>          5. 清除缓冲区的第一个包头和包体信息,做法一般是将缓冲区剩下的数据拷贝到缓冲区首部覆盖“第一个包头和包体信息”部分即可。</p><p>粘包、半包处理具体实现很多朋友都有自己的做法,比如最前面贴出的链接,这里我也贴出一段参考:略。</p>

猜你喜欢

转载自blog.csdn.net/qq_37051576/article/details/79756658