×

TCP/IP知识总结

96
纵横而乐
2017.04.27 20:55* 字数 6297

数据链路层

任务有3:发送和接收IP数据包,ARP请求和应答,RARP请求和应答
以太网数据链路帧的封装格式是6字节目的硬件地址,6字节源硬件地址,2字节类型,接着是46-1500字节的数据,随后是4字节的CRC校验码。其中类型字段中有3种情况,0800,0806,8035分别代表IP数据报,ARP请求或应答,RARP请求或应答
另一种需要提及的数据链路协议是拨号上网经常使用的PPP协议,其是在串行线路上封装IP数据包的方法。而PPP之中又包含建立,配置及测试网络的链路控制协议LCP,及针对不同网络层协议的网络控制协议NCP。
每个PPP协议帧以7E标志开头,接着是0xFF的地址字段,0x03的控制字段,2个字节的协议字段(0x0021,0xc021,0x8021分别代表其后搭载的是IP数据报,链路控制即LCP数据,网络控制即NCP数据),通常是PC用户发起PPP呼叫,响应此呼叫有服务器,比如路由器响应后完成,完成物理连接。随后PC与远程服务器之间开始LCP数据的传送,协商采用的PPP参数,如果这一步要求进行认证,则开始认证过程,比如用户名密码等。认证不成功的则关闭链路回到空闲状态;若认证成功则交换NCP分组进行网络层配置。配置完成之后逻辑通信链接已经建立,即开始上层数据的交换。
以太网MTU的大小为1500,不包括帧头部和尾部

IP

IP首部

注:网络字节序为大端,低字节放高字节数据
基本长度是20字节,4位版本号,首部长度是是首部点32位字的数目,因此首部最长60字节,服务类型字段包括3位优先权子字段,4位TOS字段(最小时延,最大吞吐量,最高可靠性和最小费用)及一个定为0的位。总长度字段指整个IP数据报的长度,以字节为单位,所以IP数据报最长65535字节。标识字段唯一地标识主机发送的每一份数据报,通常每发送一份数据报它的值就加1。TTL生存时间字段设置了数据报可以经过的最多路由器数,指定了数据报的生存时间,其初始值通常由源主机设置,通常为32或64。一旦经过一个处理它的路由器,它的值就减1,数据报就被丢弃,并发送ICMP报文通知源主机。首部校验和字段是根据IP首部计算的检验和码。其计算方法是首先把检验和字段置0,然后对首部每16位进行二进制反码求和(方法是将每16位取反再逐个相加,特殊的地方在于,若最高位产生进位,则在最低位进1),求得的结果存在检验和字段中。到达接收方后,同样对首部各16位进行反码求和,若所得结果为全1,则检验正确,否则说明丢弃收到的数据报,由上层去发现丢失的数据报并进行重传。
协议字段标识是使用ICMP,IGMP,TCP或者UDP在进行IP数据传送。
在后面讨论分片时再解释标志字段和片偏移。

ARP地址解析协议

以太网在发送数据到指定IP之前都需要知道其对应的硬件地址,而指定IP的主机要么在远程网络上,则数据需要发送到本地的路由器上,要么指定IP的主机在本地,则数据需要发送到本地的主机上,这两种情况都需要将IP数据报发送到本地的主机或路由器上。
ARP的目的即是将IP地址翻译为对应的物理地址

RARP

RARP用于网络启动的主机获取自己的IP,发链路层广播(目的MAC地址填写为ff:ff:ff:ff:ff:ff)请求rarp who-is {self-mac} tell {self-mac},应答以单播方式响应回来。
RARP的缺陷在于MAC与IP的映射需要事先定义好,且它是运行在链路层,相比于后来运行在应用层的DHCP可以自动分配IP,局限性就体现出来了。

ICMP

ICMP是IP层的组成部分,负责传递差错报文及其它需要注意的信息,有些ICMP报文把差错报文返回给用户进程,ICMP首部4字节,8位类型(共15种类型),8位代码(在各类型内部区分子类型),16位检验和,检验和包括整个ICMP报文。同时很重要的一点是在发送ICMP差错报文时,会携带引发差错的IP首部及其数据报前8字节,使得收到ICMP差错报文的主机可以与特定协议(IP首部中的协议字段)及用户进程(报文前8字节中的端口号,或TCP或UDP)联系起来

traceroute

traceroute结合使用了TTL和ICMP,原理是发送TTL从1开始并一直累加的UDP报文组目的主机,同时UDP报文中的目的端口设定为不可能的端口号(比如大于30000),若收到端口不可达的ICMP报文则停止发送,在端口不可达报文返回之前,通常会有一系列的超时ICMP报文返回,根据这一连串的信息诊断网络路由。

UDP

UDP首部8字节,依次是16位源端口,16位目的端口,16位UDP报文长度(包含首部),16位UDP检验和

好啦,终于到了重点啦

TCP

一个没有选择确认和否认的滑动窗口协议
TCP首部包含16位源端口,16位目的端口,32位序号,32位确认序号,4位首部长度,保留6位,URG,ACK,PSH,RST,SYN,FIN,16位窗口大小,16位检验和,16位紧急指针。首部长度字段单位为4字节,故TCP首部最长60字节。检验和的计算需要添加伪首部之后进行计算,只有当URG标志置1的时候紧急指针才有效。选项中最常见的可选字段是最长报文大小,又称MSS,通常在通信的第一个报文段(为建立连接而设置SYN标志的那个段)中指明这个选项,表明本端所能接收的最大长度的报文段。

TCP连接的建立和终止

各标志比特的意义:

URG 紧急指针有效
ACK 确认序号有效
PSH 接收方应尽快将这个报文段提交给应用层
RST 重建连接
SYN 同步序号,用来发起一个连接
FIN 发端完成发送任务
计算TCP数据的长度需要用IP首部中整个IP数据包的长度减去IP首部,TCP首部,即可得TCP数据长度,对于在数据链路帧中添加了padding字节的帧来说,IP数据包的长度不包含padding字节

建立连接的过程(3次握手)

1 请求端发送SYN指明打算连接的服务器端口号,及初始序号ISN
2 服务器端将包含服务器初始序号的SYN报文段响应给请求端,同时将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认,即一个SYN将占用一个序号
3 请求端必须要将确认序号设置为服务器的ISN加1以对服务器SYN报文段进行确认
主动发送SYN的一端将执行主动打开,接收主动打开的SYN并发回下一个SYN的另一端执行被动打开。

连接终止(4次握手)

终止连接的4次握手由TCP的半关闭造成,因为全双工的TCP连接在每个方向上必须单独地进行关闭,而发送FIN通常是应用层进行关闭的结果。
首先进行关闭的一方将执行主动关闭,另一方执行被动关闭。主动关闭一方发送FIN,主动关闭客户端到服务器的数据传送,服务器收到FIN后发送ACK并将收到的序号加1,即FIN也会占用一个序号。服务器关闭连接时同样发送一个FIN,客户端收到之后发回ACK并将序号加1。这些ACK是由TCP软件自动产生的。
最大报文段长度MSS表示TCP传往另一端的最大块数据的长度,仅包括,建立连接时,每一方都有用于通告它期望接收的MSS选项,且MSS选项只能出现在SYN报文段中。如果一方不接收另一方的MSS值,则MSS就定为默认值536字节。

TCP状态变迁图

TCP状态变迁图.png

只有当SYN_RCVD状态是从LISTEN状态进入,而不是从SYN_SENT状态(同时打开)进入,从SYN_RCVD回到LISTEN的状态变迁才是有效的。

2MSL等待状态

TIME_WAIT状态称为2MSL等待状态,每个具体TCP实现必须选择一个报文段最大生存时间MSL,RFC793标注的MSL为2分钟,但现实常用值为30秒,1分钟或2分钟。
MSL的意义在于:TCP执行主动关闭时,发送了最后的ACK之后,连接必须在TIME_WAIT状态停留的时间为2倍MSL,这样可以让TCP再次发送最后的ACK以防最后的ACK丢失(另一端超时并重发最后的FIN)。同时连接处于2MSL等待状态时,任何迟到的报文段将被丢弃。
处于2MSL等待状态的插口是不能被使用的,但客户端通常会做更严格的限制即所使用的端口在些期间也不可用。对于 客户端程序来说,一个本地端口不可用还不是什么大问题,但对于服务器程序来说,通常使用的是知名端口号,如果主动关闭,通常在1到4分钟之内,此端口都不可用,在这期间重启服务器程序通常会被告知address already in use。即使使用SO_REUSEADDR强制使用2MSL中的端口,由于插口对(本地IP,本地端口,远端IP,远端端口)仍处于2MSL等待状态,仍然不可能连接成功到远端相同的IP和端口,即此插口对仍无法使用。

平静时间

对于有TCP连接处于2MSL等待状态的主机,如果出现故障重启,则有可能使用重启前的2MSL等待状态的插口建立新的连接,则有可能将这个插口迟到的报文错误地当作重启后新连接的报文段,为防止这种情况,TCP在重启后的MSL秒内不能建立任何连接。这称为平静时间。

FIN_WAIT_2状态

半关闭状态的FIN_WAIT_2,如果另一端一直处于CLOSE_WAIT而不发FIN,则可能一直保持这个状态。

复位报文段

若报文段发往的连接出现错误,TCP会发出一个复位报文段RST:
常见情况是连接请求到达时目的端口没有进程在听(相比于UDP会产生一个ICMP端口不可达信息,TCP会产生复位)
异常终止一个连接时,即通过非FIN的方式异常释放。异常终止连接对应用程序来说有两个优点:1 丢弃任何待发数据并发送复位报文段 2 RST接收方判断另一端执行的是异常关闭还是正常关闭,因为API会提供异常关闭的手段。SO_LINGER选项提供了异常关闭的能力。
需要注意的是RST报文段不会导致另一端产生任何响应,另一端根本不进行确认,而是终止该连接,并通知应用层连接复位。
第三种情况是对于因掉电等原因而异常终止连接,而对端却不知道的情况,比如服务器因掉电而重启,在重启前并不会发出FIN,导致客户端程序并不会关闭连接,而在服务器重启之后收到客户端沿此连接发送来的报文段时,由于服务器并不知道此连接的存在,TCP的原则是接收方以复位作为应答。

同时打开

对于两端几乎同时发送SYN并随后都进入SYN_SENT状态,在收到另一端的SYN后,两端均再次发送SYN并对收到的SYN进行确认,并在收到对方的SYN+ACK时完成连接的建立。


TCP同时打开

同时关闭

对于两端同时发出FIN的情况,在收到对端的FIN后,两端都会发送ACK并从FIN_WAIT_1变迁到CLOSING这个同时关闭的状态,最后在收到对端的ACK后变迁到TIME_WAIT


�同时关闭

TCP交互数据流

对于当前互联网中 一半TCP分组为成块数据,一半TCP分组为交互数据,而成块数据与交互数据 字节数的比例为9:1,TCP使用不同的算法处理这两类数据。
通常TCP在接收到数据时并不立即发送ACK,而是推迟发送以便将ACK与需要沿该方向发送的数据一起发送,即如果在时延到来前有数据要发送,则捎带上ACK,否则等时延到达时再发送ACK。而时延的规则是每200毫秒超时一次,如果有ACK需要发送则会发送ACK,而此时延的起始时间是取决于TCP实现而独立于任何TCP连接的,时延按RFC规定要小于500ms,通常是200ms。

Nagle算法

对于每次发送只有一个字节的小分组,在局域网上通常不会引起麻烦,但在广域网上会增加拥塞出现的可能。一种简单的方法是Nagle算法,其要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。而且TCP会收集这些少量分组,并在确认到来时以一个分组的方式发出去。其优点是确认到达得越快,数据发送得就越快。

TCP成块数据流

成块数据使用滑动窗口协议这种流量控制方法,允许发送方在停止并等待确认前可以连续发送多个分组,加速数据的传输。

滑动窗口

窗口左右边沿的运动:
1 称窗口左边沿向右边沿靠近为窗口合拢,发生在数据被发送和确认时
2 窗口右边沿向右移动时将允许发送更多数据,发生在另一端接收进程读取已经确认的数据并释放了TCP的接收缓存时
3 右边沿向左移动时,称为窗口收缩,RFC强烈建议不要使用这种方式,但TCP必须能够在某一端产生这种情况时进行处理。
同时左边沿不可能向左边移动,如果收到一个指示窗口左边沿向左移动的ACK,会被认为是一个重复ACK并被丢弃。

窗口大小

PUSH

发送方使用该标志通知接收方将所收到的数据全部提交给接收进程,这里的数据包括与PUSH一起发送的数据以及接收方TCP已经为接收进程收到的其它数据。目前大多API没有向应用程序提供通知其TCP设置PUSH标志的方法,如果待发送数据将清空发送缓存,则大多数源于伯克利的实现能够自动设置PUSH标志,而且源于伯克利的实现一般从不将接收的数据推迟交付给应用程序,因此它们忽略所接收的PUSH标志。

慢启动

在局域网中,发送方一开始便向网络发送多个报文段,直至达到接收方通告的窗口大小为止这种方式是可以的,但如果发送方与接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题,一些中间路由器必须缓存分组,并有可能耗尽存储器的空间。
针对此的慢启动算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。慢启动为发送方的TCP增加了另一个窗口:拥塞窗口,记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通知的报文段大小 )。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与能行窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。

紧急方式URG

URG使一端告诉另一端有些“紧急数据”已经放置在普通数据流中,另一端被通知这个紧急数据已经被放置在普通数据流中,由接收方决定如何处理。
在URG置位的TCP首部中,16位的紧急指针置为正偏移量,该偏移量与TCP首部中的序号字段相加,以便得出紧急数据的最后一个字节的序号。而接收方TCP必须通知进程何时收到紧急指针及紧急指针是否在此连接(此后的讨论是基于在同一连接上),而接收进程需要做的就是读取数据流,并必须能够被TCP告知进程何时碰到紧急数据指针。
而且应用程序处于紧急方式的判定方式是,接收方当前读取位置到紧急数据指针之间只要有数据存在,即认为应用程序处于“紧急方式”,紧急指针通过之后应用程序才转回正常方式。

TCP超时和重传

预防数据报可能丢失的超时和重传策略的关键在于怎样决定超时间隔和如何确定重传频率。
对每个连接,TCP管理4个不同的定时器:
1 重传定时器用于当希望收到另一端的确认
2 坚持定时器 使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口
3 保活定时器检测到一个空闲连接的另一端何时崩溃或重启
4 2MSL定时器测量一个连接处于TIME_WAIT状态的时间

往返时间测量

TCP超时重传中最重要的部分就是对TCP连接往返时间RTT的测量,TCP会跟踪RTT的变化并相应地改变其超时时间。
最初RTT计算方案是使用低通过滤器 R = xR+(1-x)M (x取0.9,M为新rtt),而超时重传时间RTO 为 2R
但为应对RTT起伏很大的情况,使用更适当的均值偏差计算法:

拥塞避免算法

算法假定由于分组受损坏引起的丢失是非常少的,因此分组丢失就意味着在源主机和目的主机之间某处网络上发生了拥塞,有两种分组丢失的指示:发生超时和接收到重复的确认。
拥塞避免算法和慢启动算法是两个独立的算法,但当拥塞发生时,希望降低分组进入网络的传输速率,于是可以调用慢启动来实现这一点。
拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口和一个慢启动门限ssthresh,算法工作过程如下:
1 对一个给定连接,初始化cwnd为1个报文段,ssthresh为65535个字节
2 TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小
3 当拥塞发生时(超时或者收到重复确认),ssthresh被设置为当前窗口的一半(取min<cwnd,接收方通告窗口>的一半,但最少为2个报文段),此外,如果超时引发了拥塞,则cwnd被设置为1个报文段
4 当新的数据被对方确认时,就增加cwnd,但增加的方法取决于当前是否在进行慢启动或者拥塞避免。如果cwnd小于等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。
拥塞避免算法要求每次收到一个确认时将cwnd增加1/cwnd个报文段,即一个报文段。

快速重传和快速恢复算法

我们知道,在收到一个失序的报文段时,TCP立即需要产生一个ACK(一个重复的ACK)这个重复的ACK目的在于让对方知道收到一个失序的报文段,并告诉对方自己希望收到的序号。
由于重复的ACK可能是丢失报文段引起,也可能是仅仅出现了几个报文段的重新排序,但由于重新排序的报文段最多只可能产生1-2个重复的ACK。
所以如果一连串收到3个或3个以上重复的ACK,就非常可能是一个报文段丢失了,于是就可以重传丢失的数据报文段,而无需等待超时定时器溢出。这就是快速重传算法。接下来执行的不是慢启动而是拥塞避免算法,这就是快速恢复算法。
3个ACK的情况没有执行慢启动的原因是由于收到重复的ACK不仅告诉我们一个分组丢失了,而且由于接收方只有在收到另一个报文段时才会产生重复的ACK,而该报文段已经离开网络并进入了接收方的缓存,即收发端仍然有流动的数据,所以此时并不想执行慢启动来突然减少数据流,算法:
1 当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半,重传丢失的报文段,设置cwnd为ssthresh加上3倍的报文段大小。
2 每次收到另一个重复的ACK时,cwnd增加1个报文段大小并发送1个分组
3 当下一个确认新数据的ACK到达时,设置cwnd为ssthresh,这个ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认

achievement list
Web note ad 1