http://blog.csdn.net/chexlong/article/details/6123087
TCP 协议是一种面向连接的,为不同主机进程间提供可靠数据传输的协议。TCP 协议假定其所使用的网络栈下层协议(如IP 协议)是非可靠的,其自身提供机制保证数据的可靠性传输。在目前的网络栈协议族中,在需要提供可靠性数据传输的应用中,TCP 协议是首选的,有时也是唯一的选择。TCP 协议是在最早由Cerf 和Kahn[1]所提出的有关网络数据包传输协议的概念之上建立的。TCP 协议被设计成符合分层协议结构,工作在ISO/OSI 七层网络模型中的传输层中,使用网络层协议(如最常见的IP 协议)提供的服务。网络层协议尽最大努力传输上层提供的数据但并不保证数据传输的可靠性。可靠性保证必须由上层协议(如TCP 协议)提供。网络层协议主要完成的工作有:
1> 实现不同网络(主机)间的数据包路由传递。
2> 在发送端(或中转站)提供数据包分片功能以使数据包大小满足 PMTU(Path-MTU)。
3> 在接收端提供数据包分片重组功能。
4> 负责数据包优先级,安全性等问题。
传输层协议(主要针对 TCP 协议而言)主要完成的工作有(并非所有的传输层协议都需要提供这些功能如UDP 协议就不提供可靠性数据传输):
1> 提供多路复用。
2> 实现数据基本传输功能。
3> 建立通信通道。
4> 提供流量控制。
5> 提供数据可靠性传输保证。
数据可靠性传输保证是其中最为重要的方面,也是TCP 协议区别于其它协议的最重要特性。所谓提供数据可靠性传输不仅仅指将数据成功的由本地主机传送到远端主机,数据可靠性传输包括如下内容:
1> 能够处理数据传输过程中被破坏问题。
2> 能够处理重复数据接收问题。
3> 能够发现数据丢失以及对此进行有效解决。
4> 能够处理接收端数据乱序到达问题。
1.TCP 协议可靠性数据传输实现基本原理
TCP 协议必须提供对所有这些问题的解决方案方可保证其所声称的数据可靠性传输。TCP协议规范和当前绝大多数TCP 协议实现代码均采用数据重传和数据确认应答机制来完成TCP 协议的可靠性数据传输。数据超时重传和数据应答机制的基本前提是对每个传输的字节进行编号(好像有问题,应该是传输的每个包),即我们通常所说的序列号。数据超时重传是发送端在某个数据包发送出去,在一段固定时间后如果没有收到对该数据包的确认应答,则(假定该数据包在传输过程中丢失)重新发送该数据包。而数据确认应答是指接收端在成功接收到一个有效数据包后,发送一个确认应答数据包给发送端主机,该确认应答数据包中所包含的应答序列号即指已接收到的数据中最后一个字节的序列号加1,加1 的目的在于指出此时接收端期望接收的下一个数据包中第一个字节的序列号。数据超时重传和数据确认应答以及对每个传输的字节分配序列号是TCP 协议提供可靠性数据传输的核心本质。
1)数据确认应答数据包中应答序列号的含义
应答序列号并非其表面上所显示的意义,其实际上是指接收端希望接收的下一个字节的序列号。所以接收端在成功接收到部分数据后,其发送的应答数据包中应答序列号被设置为这些数据中最后一个字节的序列号加一。所以从其含义上来说,应答序列号称为请求序列号有时更为合适。应答序列号在TCP 首部中应答序列号字段中被设置。而TCP 首部中序列号字段表示包含该TCP 首部的数据包中所包含数据的第一个字节的序列号(令为N)。如果接收端成功接收该数据包,之前又无丢失数据包,则接收端发送的应答数据包中的应答序列号应该为:N+LEN。其中LEN 为接收的数据包的数据长度。该应答序列号也是发送端将要发送的下一个数据包中第一个字节的序列号(由此亦可看出上文中将应答序列号称为请求序列号的原因所在)。
2)数据确认应答中的累积效应
TCP 协议中接收端对所接收数据的应答是累积的。累积的含义有二:
1>应答序列号是逐渐递增的,这与发送端数据编号是递增的相吻合。
2>不可进行跨越式数据应答。
所谓不可进行跨越式数据应答,可以以数据包乱序到达为例进行说明。如果由于发送端所选择传输路径的不同,较后发送的序列号较大的数据包先到达接收端,而先发送的序列号较小的数据包由于线路问题(或路由器故障)被暂时延迟在网络中,此时接收端不可对这些序列号较大的数据进行应答。如果接收端需要发送一个应答数据包,则应答序列号仍然应该设置成对序列号较小的数据包的请求(注意应答序列号指的是接收端希望接收的下一个字节的序列号,故在数据传输过程中将应答数据包称为数据请求数据包更为合适)。举例来说,如果接收端目前的应答序列号为201,表示接收端正在等待发送端发送从201 开始编号的数据,之后发送端连续发送了两个数据包,第一个数据包中数据序列号范围为201-300,第二个数据包中数据序列号范围为301-400。如果由于选择了不同的传输路径造成第二个数据包最先到达接收端,而第一个数据包在网络中延迟了一段时间,则接收端不可对第二个数据包进行应答,即不可发送应答序列号为401 的确认应答数据包,而是不断发送应答序列号为201的应答数据包直到该序列号的数据到达。我们通常所说的快速重传机制即发送端在连续接收到3 个相同序列号的应答数据包后需要立刻重传应答序列号所表示的数据。因为此时表示极有可能出现了数据包丢失的情况,如上例中第一个数据包如果丢失在网络中并且发送端重传的相同数据包由于选择相同的线路也未能到达接收端,则接收端将不断发送应答序列号为201 的应答数据包而不会将应答序列号设置为401。注意此时接收端已接收到序列号从301-400 的数据。
3)重传应答机制与序列号结合:
1> 能够处理数据在传输过程中被破坏的问题。
首先通过对所接收数据包的校验,确认该数据包中数据是否存在错误。如果有,则简单丢弃或者发送一个应答数据包重新对这些数据进行请求。发送端在等待一段时间后,则会重新发送这些数据。本质上,数据传输错误的解决是通过数据重传机制完成的。
2> 能够处理接收重复数据问题。
首先利用序列号可以发现数据重复问题。因为每个传输的数据均被赋予一个唯一的序列号,如果到达的两份数据具有重叠的序列号(如由发送端数据包重传造成),则表示出现数据重复问题,此时只须丢弃其中一份保留另一份即可。多个数据包中数据重叠的情况解决方式类似。本质上,数据重复问题的解决是通过检查序列号完成的。
3> 能够发现数据丢失以及进行有效解决。
首先必须说明,此处数据包丢失的概念是指在一段合理时间内,应该到达的数据包没有到达,而非我们平常所理解的永远不到达。所以数据包丢失与数据包乱序到达有时在判断上和软件处理上很难区分。数据丢失的判断是猜测性的,我们无法确定一个数据包一定丢失在传输过程中,大多是被延迟在网络中,即实质的问题只是数据包乱序到达。将二者区分开来的一个主要依据是在合理的时间内,由这个可能丢失的数据包所造成的序列号“空洞“是否能够被填补上。可能的数据丢失一个显然的结果是在接收端接收的数据出现序列号不连续现象。如接收端只接收到序列号从1 到100 的数据包,之后又接收到序列号从200 到300 的数据包,而且在一段合理的
时间内(由此基本排除乱序问题),序列号从101 到199 的数据一直未到达,则表示包含序列号从101 到199 的数据包在传输过程中很可能丢失(或者有极不正常的延迟)。对数据包是否丢失判断的另外一个干扰因素是发送端的重传机制,如果一个序列号较前的数据包在网络中丢失,造成序列号较后的数据包提前到达接收端,也会暂时造成序列号不连续,但由于发送端在没有接收到确认应答时,会重新发送序列号较前的那个数据包,如果此后接收端接收到一个重传的数据包,则仅仅只会在接收端造成数据包乱序到达的表象。无论实质如何,如果软件实现判断出数据包丢失,则接收端将通过不断发送对这些丢失的数据的请求数据包(也即应答数据包,见前文中对数据应答数据包和数据应答累积效应的说明)来迫使发送端重新发送这些数据。通常发送端自身会自发的重传这些未得到对方确认的数据,但由于重传机制采用指数退避算法,每次重传的间隔时间均会加倍,所以通过发送方主动重传机制恢复的时间较长,而接收端通过不断发送对这些丢失数据的请求,发送端在接收到三个这样的请求数据包后(三个请求数据包中具有同一个请求序列号--也即前文中所说的应答序列号),会立刻触发对这些数据的重新发送,这称为快速恢复或者快速重传机制。本质上,对于数据丢失问题的解决是通过数据重传机制完成的。在此过程中序列号和数据确认应答起着关键的作用。
4> 能够处理接收端数据乱序到达问题。
如果通信双方存在多条传输路径, 则有可能出现数据乱序问题,即序列号较大的数据先于序列号较小的数据到达,而发送端确实是按序列号由小到大的顺序发送的。数据乱序的本质是数据都成功到达了,但到达的顺序不尽如人意。对这个问题的解决相对比较简单,只需对这些数据进行重新排序即可。本质上,对数据乱序问题的解决是通过排序数据序列号完成的。
2.TCP 协议可靠性数据传输软件实现基本原理
由上文可见,序列号,数据超时重传和数据确认应答机制保证了 TCP 协议可靠性传输的要求。由于需要对所发送的数据进行编号,又需要对接收的数据进行应答,所以使用TCP 协议的通信双方必须通过某种机制了解对方的初始序列号。只有在确切知道对方的初始序列号的情况下,才能从一开始对所接收数据的合法性进行判断。另外还需要在本地维护一个对方应答的序列号,以随时跟随对方的数据请求。在最后通信通道关闭时,可以确知本地发送的数是否已被对方完全接收;此外这个对方应答序列号在控制本地数据通量方面也发挥着重要的作用:用本地发送序列号减去对方应答序列号则可以立刻得知目前发送出去的数据有多少没有得到对方的应答。
综上所述,可靠性传输要求通信双方维护如下序列号:
SND.NXT本地将要发送的下一个序列号。该变量对应TCP 首部中序列号字段。表示该数据包中所包含数据的第一个字节的序列号。每次发送一个数据包,该变量都需要进行更新:SND.NXT = SND.NXT + 本次发送的数据包中包含的数据长度SND.ACKED
对方对本地所发送数据到目前为止进行了应答的序列号,换句话说,SND.ACKED+1 表示本地已发送出去但尚未得到对方应答的数据集中对应的第一个(最小的)序列号。RCV.NXT本地希望接收的下一个序列号。该序列号被称为应答序列号,也可称为请求序列号,在本地发送的应答报文中,TCP 首部中应答序列号字段即设置为该变量的值,表示本地希望从对方接收的下一个字节的序列号。
图 1(上图)显示了TCP 首部格式。序列号字段对应前文中SND.NXT 变量,应答序列号字段对应前文中RCV.NXT 变量。ACK 标志位设置为1 表示这是一个应答数据包。实际上对于 TCP 协议而言,在成功建立连接后,此后发送的所有数据包的ACK 标志位均被设置为1,即在传送正常数据的同时传送应答,如此处理可以减少网络中传输的数据包数量。
3.TCP 协议建立连接的必要性
图 1TCP 首部格式中SYN 标志位仅使用在建立TCP 连接的过程中,TCP 建立连接的过程被称为“三路握手“连接,即一般通信双方共需要传输三个数据包方能成功建立一个TCP 连接。我们通常将建立连接作为使用TCP 协议理所当然的前导过程,但很少去质疑这样一个建立连接过程的必要性。实际上,在上文中已经做出部分解释,使用TCP 协议必须首先建立一个连接是保证TCP 协议可靠性数据传输的基本前提(当然由于TCP 协议是一个有状态协议,必须通过某种机制进行通信双方状态上的同步,而建立连接就是这样一种机制)。至于为何需要三个数据包,原因是建立连接过程中信息的交换必须至少使用三个数据包,从下文的分析来看,建立连接最多需要使用四个数据包。需要再次提到的是:SYN 标志位只是用在建立连接的三个(或者四个)数据包中,一旦连接建立完成后,之后发送的所有数据包不可设置SYN 标志位。单从保证数据可靠性传输角度而言,TCP 协议需要在正式数据传输之前首先进行某些信息的交换,这个信息即是双方的初始序列号(另外的一些信息包括最大报文长度通报等)。
诚如前文所述,序列号的使用对于 TCP 协议而言至关重要,在正式数据传输之前,双方必须得到对方的初始字节数据的编号,这样才有可能对其所接收数据的合法性进行判断,才有其它的对数据重复,数据重叠等一系列问题的进一步判别和解决。故交换各自的初始序列号必须在正式数据传输之前完成,我们美其名曰这个过程为连接建立过程。至于双方TCP 协议各自状态的更新主要是软件设计上可靠性保证的一个辅助,并非这个所谓的建立过程所主要关注的问题。
初始序列号的交换从最直接的角度来说需要四个数据包:
1> 主机 A 向主机B 发送其初始序列号。
2> 主机 B 向主机A 确认其发送的初始序列号。
3> 主机 B 向主机A 发送其初始序列号。
4> 主机 A 向主机B 确认其发送的初始序列号。
我们将<2><3>两步合为一步,即B 向A 确认其(A 之前发送的)初始序列号的同时发送其(即B 自己的)初始序列号。所谓确认数据包即将数据包的ACK 标志位设置为1 即可。注意这三个(或四个)数据包中SYN 标志位设置为1,而且SYN 标志位也仅在这三个(或四个)数据包中被设置为1。
此处有一个问题:即A,B 主机在通报各自初始序列号的同时能否传输一些正常数据,原理上可以(TCP 协议规范上并没有说不可以),但是大多数实现在通报初始序列号时都不附带正常数据,而是将其作为一个单独的过程,由此正式确立建立连接一说。
小结
TCP 协议声称可靠性数据传输,其底层实现机制主要包括三个方面:使用序列号对传输的数据进行编号,数据超时重传,数据确认应答。本文主要阐述了此三个方面为何能够实现可靠性传输并简单解释了其中的内部机理,此后对TCP 协议可靠性数据传输实现提出了其基本原理,并在文章最后从保证可靠性数据传输的角度简单阐述了使用TCP 协议时为何需要一个建立连接的过程。TCP 协议是TCP/IP 协议族中较为复杂的一个,其复杂性的最主要来源之一即其需要提供可靠性数据传输,本文旨在对TCP 协议保证可靠性数据传输的基本实现原理介绍中降低读者对TCP 协议理解的复杂度。
注:本文来自《Linux内核网络栈源代码情景分析》