曲径通幽论坛
标题:
TCP 数据的处理:流,段 和 序列号
[打印本页]
作者:
beyes
时间:
2013-1-16 20:38
标题:
TCP 数据的处理:流,段 和 序列号
使用离散的消息块是相当简单的,许多协议都直接使用它们。但其天生存有局限性,比如为了能够正常的会话,它会迫使应用程序去创建这些离散的数据块。然而,许多应用程序在一定程度上需要连续的发送消息,也就是说还让应用程序分神去创建这这些大量的消息块是不适宜的。另外,一些应用程序需要发送大把的数据,对于较低的协议层来说,它们不可能接受一次性将大量的数据当作整块来发送。
如使用像 UDP 这样的协议,应用程序将不得不人为地将它们的数据分割成许多消息块,而它们之间并没有什么内在的意义。这样就会使得应用程序需要做许多额外的工作,比如不得不对这些消息中的数据进行跟踪,如果发现丢失了,还得给补上;此外,应用程序还得在接收到这些消息时负责按正确的顺序将它们重组好,因为 IP 协议发送这些消息块时很可能不是按照一定的顺序的。当然了,应用程序确实可以完整这些工作,但是这些功能 TCP 协议自身就可以实现,因此再用应用程序来完成这些事情,那就显得毫无意义。
TCP 设计者使 TCP 协议可以接收来自应用程序任意大小,任意结构类型的数据,而不要求数据必须是一个个的消息块。更具体的说,TCP 对来自应用程序的数据是以
”流“(stream)
的形式来看待的。因此,对 TCP 的描述也是从”面向数据流“(stream-oriented)的。每一个应用程序将数据以一个稳定以字节流来传输,而不必将它们先搞成数据块,也无需担心这个流到底有多长。形象的说,就像从井里用水泵打水一般,水从井里到泵口,是一种流的形式;而出泵口后,至于你是用大桶还是小桶来装水,那就不是应用程序所关心的问题了。
将数据打包成”段“(segments)
从应用程序送来的数据经 TCP 处理后,会使用网络层的 IP 协议将其发送。IP 是一种面向消息(message-oriented)的协议,而非面向数据流(stream-oriented)。因此,来自应用程序的数据流就会在 TCP 里被封装成一个个的消息块,这些消息块我们称之为
”TCP 段“(TCP segments)
。
”TCP 段“ 被送往 IP 后,会进一步被封装以 IP 数据包头,然后再将其传送到目的设备。在接收端,数据经过 IP 层时,会被解包成 ”TCP 段“,然后再送到 TCP ,再接着 TCP 会将这些数据还原成字节流返回给应用程序。工作原理如下图所示:
[attach]1217[/attach]
上图中,一旦 TCP 连接建立起来,一个应用层协议就可以给 TCP 发送一个稳定的字节流,对于该流,不要求符合任何一个特定结构。TCP 会对将这些字节打包成”段“,而”段“的大小则由一些不同的参数来决定。接着,这些”段“会被送往 IP 层,在此还会进一步封装成 IP 数据包,然后再传送出去。在接收端,执行与发送端相反的解包 过程。
TCP 数据标识符:序列号(Sequence Numbers)
由于 TCP 是一种可靠协议,它需要保持对所有来自应用程序数据的跟踪,这样才能确保接收端可以完整无缺的收到数据。此外,它还必须确保数据按照它所发送数据被顺序接收,且必须重发任何意外丢失的数据。
TCP 对数据的跟踪涉及到一个重要概念,即“序列号”(Sequence Numbers) 。由于 TCP 是面向数据流的协议,因此“序列号”必须能够体现到每一个数据字节!这看起来貌似很奇怪,但实际上确实如此。序列号可以确保以“段”形式发送的数据可以重新还原为传递给应用层的数据流。
下面使用一个实例来说明序列号的情形,这里使用嗅探工具来捕捉一个访问
www.groad.net
时所需要的 HTTP 协议,而 HTTP 协议则是基于 TCP 的。
在三次握手开始时,客户端向服务端发送 SYN ,此时可以看到:
[attach]1218[/attach]
上图中,所发送的序列号是 4131116864 ,底下的应答号(Ack Number 为 0)。该序列号由客户端随机产生。
接着,服务器应答客户端(SYN + ACK) :
[attach]1219[/attach]
由上图可见,服务器应答号为 4131116864 + 1 = 4131116865 ;服务器则自己随机产生了一个序列号 3152946212 。
接着,客户端回应服务器(ACK):
[attach]1220[/attach]
由上图可见,客户端将之前服务器发来的序列号 3152946212 加 1 后作为应答号,并将服务器发来的应答号作为自己的序列号。
经过上面的几个步骤,3 次握手完成。接下来,客户端要用 GET 方法来请求服务器上的数据了:
[attach]1221[/attach]
我们再观察这条记录中的序列号和应答号:
[attach]1222[/attach]
发现它们和第 3 次握手中的一样。需要注意的是,[Next Sequence Number] 中所条的序列号并不是本次发送的任何一次数据,而是嗅探软件自己计算好下一次要发送的序列号值,因此这里使用一个中括号 [ ] 提示。那么 ,[Next Sequence Number] 这个值(4131117137)是怎么算出来的?回过头来看一下上面客户端使用 GET 请求的图,那次发送的数据尺寸为 330 。用这个值减去以太网包头 14 个字节,IP 包头 20 个字节,TCP 包头 20 个字节,以及 FCS ()4 个字节,共 58 个字节后得到 272 。把 4131116865 加上 272 后就得到 4131117137 了。
接下来服务器应答客户端的请求:
[attach]1223[/attach]
从上面可以看到,服务器发送过来的序列号为 3152946213 ,即上一步客户端发来的应答号;而它所发送的应答号就为 4131117137 。也就是说,服务器告诉客户端:“我已经收到了你发来的 272 个数据”。
再接下来,服务器真正的发送 HTTP 数据,包括 HTTP 头部,HTTP 网页内容:
[attach]1224[/attach]
[attach]1225[/attach]
就是这样,客户端和服务器之间就是一发送一应答确保数据发送接收的完整性。在某次得到的 TCP 数据中,将本次的序列号减去上一个序列号,那么就得到本次接收到的数据长度,从而就可以还提取出接收过来的数据,最后还原回应用层所需要接收的数据流。
欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/)
Powered by Discuz! X3.2