tcpip技术详解
TCP报⽂段⾸部格式如下图所⽰:
常⽤的选项段包括:1.窗⼝扩⼤选项 2.时间戳选项 3.MMS选项等
tcpip协议pdf
需要注意的事:
1.序号是以字节为单位,因此最多可以表⽰2^32-1个字节,到达最⼤值以后从0开始
2.并不是每⼀个报⽂段都携带数据
3.TCP的源地址和⽬的地址包含在其外部的IP⾸部中,所以TCP⾸部
只有源端⼝和⽬的端⼝信息
4.校验和覆盖了整个TCP报⽂段,包括TCP⾸部和TCP数据
TCP的⾸部格式为其⾯向连接的可靠传输提供了保证。
1.地址/端⼝⼆元组确定了报⽂发往的主机(地址)及应⽤程序(端⼝)。
2.每⼀个报⽂段都有⼀个校验和,这可以保证被传输层接受的数据的正
确性。如果校验失败,则该报⽂段会被丢弃。接收端不进⾏确认,发送
端待重传定时器超时时进⾏重传
3.每个TCP报⽂段都有⼀个32位的序号,它标识本报⽂段的第⼀个字节。
为每⼀个字节编号可以解决报⽂因延时⽽乱序到达的问题,保证了被传
输层上传给应⽤程序的数据都有⼀个正确顺序。⼀般⽽⾔如果乱序的报
⽂段到达接收端,接收端会先缓存这个报⽂段,但是不会确认它。当它
编号之前的数据正确到达后,接受端才会确认这个乱序的报⽂段。
4.每个TCP报⽂段中都有⼀个确认号。确认序号包含发送确认的⼀端所
期望收到的下⼀个序号。因此确认序号是上⼀次已成功收到数据字节序
号加1.只有ACK标志为1时,确认序号字段才有效。
5.⾸部长度即TCP⾸部的长度,20-60字节不等。⼀般情况下长度是20字节
6.16字节窗⼝⼤⼩是为进⾏流量控制所设置的。如果不进⾏流量控制,⼀旦
发送端和接收端数据发送速率存在很⼤差异,那么很有可能⼀⽅的接受缓
冲已经被填满,⽽另⼀端毫⽆所知,仍在不停发送数据,显然这种情况下
会⼤量丢包,重传,进⽽造成⽹络拥塞。
Nagle算法的名字来源于其发明者 John Nagle,该算法主要⽤于避免过多⼩分节报⽂在⽹
络中传输,从⽽降低⽹络容量利⽤率。⽐如⼀个20字节的TCP⾸部+20字节的IP⾸部+1个
字节的数据组成的TCP数据报,有效传输通道利⽤率只有将近1 /40。如果⽹络充斥着这样
的⼩分组数据,则⽹络资源的利⽤率是相当低下的。
Nagle算法⽤伪代码的形式可以表⽰如下:
Nagle算法要求⼀个TCP连接上最多只能有⼀个未被确认的未完成的⼩分组,在该分组的
确认到达之前不能发送其他的⼩分组。相反TCP收集这些⼩分组,并在确认到来时以⼀个
分组的⽅式发出去。
需要特别强调的是TCP不对ACK报⽂段进⾏确认,TCP只确认那些包含了数据的ACK报⽂
段。TCP/IP详解v1 P203 的例⼦对于"14,15"报⽂段与Nagle算法相违背的解释,个⼈认
为与TCP不回复ACK段有关。书上的解释可能是由于直译导致的错误。否则我真没看懂。-。-!
然⽽Nagle算法并不是所有场合都需要开启,对于⼀些需要快速响应,对延时敏感的应⽤,
⽐如窗⼝程序,⿏标响应,⼀般⽽⾔需要关闭Nagle。Socket API⽤户可以通过套接⼝
选项TCP_NODELAY来关闭该算法。
在遍布世界的互联⽹线路上进⾏可靠的数据传输谈何容易,⼀来传输介质
有差异,因此当肥胖管道中的数据洪流涌⼊瘦⼩管道时,很可能发⽣拥塞;
⼆来发送者和接受者的数据吞吐速率不⼀定匹配,很有可能发⽅太快⽽收
⽅太慢,导致丢包重传频发;三来在⼴域⽹⾼速远距离传输时,TCP数据报
⽂的出错⼏率相对于局域⽹要⾼得多。当然TCP⾯临的问题不仅仅只有以
上三点。本⽂所阐述的内容主要是第⼆点——TCP的流量控制问题。
TCP使⽤滑动窗⼝协议来实现流量控制。该协议允许发送⽅在停⽌并等待
确认前可以连续发送多个分组。由于发送⽅不必每发⼀个分组就停下来等
待确认,因此该协议可以加速数据传输。
TCP连接的两端各维护了⼀个发送窗⼝和⼀个接收窗⼝,发送⽅窗⼝的⼤
⼩由接收⽅确定,⽬的在于控制发送速度,以免接收⽅的缓存不够⼤,⽽
导致溢出,同时也可以避免⽹络拥塞。如图所⽰,接收⽅通告的窗⼝成为
提出的窗⼝(offered window),它覆盖了第4字节到第9字节的区域。它表
明第4字节之前的数据已经发送并被对端确认,4-6为已经发送但未被确认
的字节,7-9是等待发送的字节,通告窗⼝的⼤⼩为6,这个窗⼝值是由接
收端告知的。当接收⽅确认数据后这个滑动窗⼝不停地向右移动。
需要强调的是:虽然窗⼝⼤⼩可减⼩,但是Host Requirements RFC强烈
建议不要使窗⼝的右边界向左边移动
在⼴域⽹,TCP报⽂可能要经过多个路由器和速率较慢的链路。如果发送⽅⼀开始就向⽹络
发送多个报⽂段,则中间路由器的缓冲负担会⽴刻加重,很可能致使路由器缓存空间耗尽,引发丢包。
举例说明:假设中间路由存储空间可以缓存3个报⽂段(这是作了简化的假设),发送端连续发出了编号为1,2,3,4,5的报⽂段,中间路由可能来不及⼀次性全部转发,于是先将转发1和2,缓存3,4,5,此时路由器存储空间耗尽。这时发送⽅⼜有新报⽂6,7到达,于是来不及转发的3, 4被丢弃,6,7被缓存,这样不可避免地导致了重传发⽣。
为了解决这⼀问题,TCP需要⽀持“慢启动”算法,该算法通过观察到新分组进⼊⽹络的速率应该与另⼀端返回确认的速率相同⽽进⾏⼯作。
慢启动算法在发送⽅TCP增加了⼀个拥塞窗⼝,记为cwnd,它与通告窗⼝相互配合来完成
慢启动过程。
当TCP连接建⽴成功以后,开始阶段,cwnd为1个报⽂段。发送⽅每收到⼀个ack,cwnd都会增加。收到第⼀个ack时,cwnd = 2;收到第2个ack时,cwnd = 4,如此这般以指数关系增加,直到cwnd = 通告窗⼝或者发送报⽂的速率达到了⽹路的容量,中间路由器开始丢弃
分组,这就通知发送⽅拥塞窗⼝开得过⼤。
需要注意的是:
1.发送⽅发送上限是cwnd和通告窗⼝的最⼩值(单位是字节)。
2.cwnd以字节为单位,但是慢启动以报⽂段⼤⼩为单位进⾏增加
TCP实现可靠传输有赖于其超时/重传机制,但这种机制并⾮总是那么可靠。
设想⼴域⽹上⽹络拥塞,某些报⽂传输延时较长(但并未丢失),此时发送端
重传计时器超时,于是重传报⽂,导致⽹络上存在两个同样的报⽂,很显然
降低了带宽资源的利⽤率,加剧⽹络拥塞程度,类似⽕上浇油。
因此TCP需要拥塞控制能⼒,jacobson,1988描述了⼀种拥塞避免算法,后
来成为了当时的标准。该算法认为分组在传输过程中因损坏⽽导致丢失的可
能性⾮常⼩,因此分组丢失就意味着⽹络拥塞。有两个现象表明⽹络发⽣拥
塞它们是: 1.发⽣超时 2.收到重复确认
尽管慢启动算法和拥塞避免算法是两个⽬的不同的独⽴算法,但是拥塞避免
算法的实现借助了慢启动算法。曾记否?慢启动算法在通告窗⼝的基础上增
加了⼀个拥塞窗⼝cwnd,通告窗⼝由接受端使⽤,⽽拥塞窗⼝由发送端使⽤。
发送⽅每次所能发送的最⼤报⽂段为cwnd和通告窗⼝⼤⼩的最⼩值。拥塞控
制算法在cwnd窗⼝的基础上⼜增加了⼀个称之为慢启动门限的变量ssthresh,
其初始值为65536.
当拥塞发⽣时,ssthresh被设置为当前通告窗⼝⼤⼩的⼀半(但最⼩为2个报⽂
段),如果是超时引起的则cwnd变为1。当新的数据被对⽅确认时,就增加cwnd
.但是如何增加取决于我们采⽤的是慢启动算法还是拥塞避免算法。这⾥的规则
是当cwnd⼩于ssthresh时,执⾏慢启动算法;当cwnd⼤于ssthresh时,执⾏
拥塞避免算法。当cwnd增加到拥塞发⽣时的通告窗⼝⼤⼩的⼀半时,慢启动过
程终⽌,接着转为执⾏拥塞避免。
这⾥cwnd在慢启动过程,仍然是以指数的⽅式增长1,。如果是拥塞避免
过程,则cwnd以每个往返(RTT)1满尺⼨数据段的速度递增。直到⽹络出现拥
塞为⽌,显然这是⼀种加性增长。
基于超时的拥塞检测需要有⼀个好的RTT算法,然⽽影响RTT的因素很多,⽽且
由于RTT具有动态特性,在实际中不易得到较准确的RTT。因此基于收到重复ACK
的拥塞检测在⽤得相对较多。快速重传算法就是基于重复ACK提出的,它规定如
果收到连续的3个或者3个以上的重复ACK则⾮常有可能是⼀个报⽂丢失了,于是
发送端重传丢失的报⽂段,⽆需等到重传定时器溢出。接下执⾏的不是慢启动算
法⽽是拥塞避免算法—这就是快速恢复算法。
那么在这种情况下为什么没有执⾏慢启动算法呢?这是出于效率的考虑,由于只
有在收到了另⼀个报⽂的时候,接收端才会发送重复的ACK,因此收到重复的ACK
不仅告诉我们⼀个分组丢失了,⽽且提⽰我们刚接到的那个报⽂已经离开了⽹络。
也就是说,收发两端仍然有数据流动,因此不必执⾏慢启动来突然减少数据流动。
快速传送和快速恢复算法经常像下⾯那样⼀起实现。
1.  当第三个重复ACK收到时,设置ssthresh为不⼤于max (FlightSize / 2, 2*SMSS) FlightSize是仍在⽹络中传送的数据量,SMSS是发送端MSS
2.  重传丢失的数据段并设置cwnd的值为ssthresh+3*SMSS。这将⼈为地按已
经离开⽹络的报⽂段数⽬(3)和接收端缓冲数据量来扩充拥塞窗⼝。
3.  对每个接收到的附加的重复ACK,将cwnd增⼤SMSS字节。这将⼈为地扩充
拥塞窗⼝以反映已经离开⽹络的附加数据段。
4.  发送⼀个数据段,如果cwnd和接收端的通知窗⼝的值允许的话。
5.  当下⼀个确认新数据的ACK到达时,设定cwnd值为ssthresh(步骤1设置的
值)。这称作“deflating"窗⼝。这个ACK必须是步骤1触发的重发引起的确认,
重发之后⼀个RTT(在接收端有了次序紊乱的数据段的情况下,它可能⼀会⼉
就到达)。另外,此ACK应该确认丢失数据段和第三个ACK副本期间的数据段
,如果它们⼀个也没有丢失的话。
定时器在TCP可靠传输的过程中起着举⾜轻重的作⽤。TCP在建⽴连接之后可能(保活
keep-alive定时器是可选的)会 启动四个定时器,分别是:
重传定时器:为了防⽌报⽂丢失或者损坏,TCP在发送⼀个报⽂以后启动重传定时器,
如果定时器溢出之前该报⽂的ACK还未到达,则重传该报⽂。重传定时器超时时间(Retransmision Timeout)依赖于往返时间RTT,⽽RTT在传输的过程中是动态变化的,
⽽且变化范围较⼤,精准的计算RTT较困难,TCP有时间戳选项,为准确的计算RTT提供
了⽅便
坚持定时器:TCP报⽂段可能是不携带任何数据的ACK分节,接收端对此分节⽆确认必要。假设有如下场景,A是发送端,B为接收端,B的通告窗⼝win⼤⼩为100字节,这时A发送100字节到B,由于A的ACK告知B A.win=0,所以,A暂停发送数据等待A.win变⼤,稍后A的接受缓存中的数据被上层应⽤
处理,于是A.win增⼤,接着A发送ACK窗⼝更新通知B,如果这个不携带任何数据的分节丢失,则A,B就会处于⼀种死锁状态(A等待B通知窗⼝更新,B等待A 发送数据)。为了解决这⼀问题,TCP发送⽅使⽤⼀个坚持定时器来周期性的向接收⽅查询,以便发现窗⼝是否已经增⼤。这些从发送⽅发出的报⽂段称为窗⼝探查(window probe)
2MSL定时器:MSL是报⽂段做⼤⽣存时间(Maximum Segment Lifetime),设置这个定时器
有两个⽬的其⼀是为了测量连接处于TIME_WAIT状态的时间.这样可以让TCP再次发送最后
的ACK以防⽌这个ACK丢失(如果丢失,另⼀端会重传FIN)。
其⼆,为允许⽼的重复分节在⽹络中消逝。具体可以解释为,如果⼀个TCP连接在断开之前
有迷途分节尚未消逝,在断开该TCP连接之后⽴刻重启⼀个同样的连接(双⽅的IP地址和端⼝port相同),这时之前的迷途的⽼分节可能对新的新的TCP连接接收,从⽽造成未定义的错误。为了避免这种情况,TCP规定在TIME_WAIT状态,不能启动⼀个连接的化⾝。既然TIME_WAIT 状态维持2MSL,这就保证了⼀个连接上的分组及其应该在2MSL内都会消失。
保活定时器:这个定时器在选项中设置,默认是关闭的。如果开启这个选项,那么如果⼀个连
接闲置了⼀定时间以后,服务端会发送探寻分节给客户端判断客户端是否有响应。这个时间在TCP的各种实现中不尽相同,但通常都在⼀⼩时以上。由于这个保活时间间隔太长,所以很多
需要频繁检测对端是否存活的应⽤都是在应⽤层⾃⾏开发⼼跳机制。博主计划近期实现⼀个简
单的⼼跳消息组件,希望到时候能够对此有更深⼊的探讨。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。