【完结^_^撒花】TCP/IP 详解 卷一:协议 笔记
【完结^_^撒花】TCP/IP 详解 卷一:协议 笔记
先注明 本文所有内容都是基于底层是以太网或iee802.3 封装的数据链路层进行讨论的,不包括点对点网络。
其中的ip协议指的也是ipv4.
数据链路层
以太网和IEEE 802封装
以太网
IEEE 802
IEEE 下的802委员会 在 以太网发布之后几年,也公布了一项组网计数,不幸的是,802.2 和802.3 定义了一个与以太网不同的帧格式。
以太网的类型字段后就是数据部分
802.3标准定义的帧和以太网的帧都有最小长度要求。以太网规定数据部分最少46字节,802.3至少为38字节。为了保证这一点,必须在不足的空间插入填充(pad)字节。
环回口/loopback
单播组播广播:分别向局域网下的单个主机/ 某一组主机 /所有主机 发送数据包。
最大传输单元 MTU
无论是以太网还是IEEE802.3 ,对数据帧都有一个长度的限制
以太网是1500字节,IEEE802.3是1492。如果ip层传来的数据包大于MTU需要进行分片。
路径MTU
当数据需要跨网络传递时,由于各个网络的链路层MTU 可能不同,为了使数据包能够一次性通过所有经过的网络,需要找到这个传递路径上的一个最小的MTU,我们称之为路径MTU。
路径MTU是通过ICMP协议,不断发送ip包,并在ip包中的标志位上标记此包无法被分片,并不断增大数据包的大小。如果超过了路径中任何一个节点就会被此节点丢弃并产生特定类型的差错报文(需要分片但是设置了不分片),这样就知道了这条路径上的MTU(上一个被发送但是没有收到差错报文的数据报大小)
几个基本概念
路由器 和 网关
网关是用来连接两个不同的网络的。
网关是网络中的一个角色,指的是一个“网络”中的出口。路由器是一种设备。
路由器可以实现网关的功能,但是路由器功能不仅仅是实现网关。
网关可以由路由器实现,但是也不仅仅是由路由器实现。
交换机
交换机实现了特定网络内的数据交换。同一台主机
ip 协议
ip协议是tcp/ip协议族中最核心的协议,协议族里其他同层或上层协议都使用ip数据报格式传送数据。该协议提供了一个无连接的不可靠的传输数据报传输服务。
无连接:ip协议并不维护后续数据报的状态信息(这个特性亦称无状态)。每个数据报是独立的,这也说明发送ip数据报并不需要按顺序接受,因为他们之间是独立的。对于同样的目的地,可能不同的数据包会选择不同的路径,导致先发的不一定先到。
不可靠: ip协议不能保证IP数据报能成功地到达目的地。IP仅提供最好的传输服务。如果发生某种错误时,如某个路由器暂时用完了缓冲区,IP有一个简单的错误处理算法:丢弃该数据报,然后发送ICMP消息报给信源端。任何要求的可靠性必须由上层来提供(如TCP)。
由上图的ip首部图可以看到,ip表示长度的bit长度为16,也就是可以说明ip的数据报最长的长度为2^16-1=65535个字节。(当然,在实际传输过程中,大多数链路层都会对其进行分片)
TTL
(time-to-live)生存时间字段设置了数据报可以经过的最多路由器数。它指定了数据报的生存时间。TTL的初始值由源主机设置(通常为32或64),一旦经过一个处理它的路由器,它的值就减去1。当该字段的值为0时,数据报就被丢弃,并发送ICMP报文通知源主机。
校验和
发送时先把每16个bit进行一次二进制反码求和,存为检验和字段。
接收方收到后也对每16个bit进行一次相同计算过程,如果没有任何差错,最后的计算结果应该为全1。如果不全1
,那么IP就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。
路由选择
IP层既可以配置成路由器也可以配置成主机。当今的大多数多用户系统,包括几乎所有的Unix系统,都可以配置成一个路由器。本质上的区别在于主机从不把数据报从一个接口转发到另一个接口,而路由器则要转发数据报。
子网寻址和子网掩码 这里直接省略了
RARP是被那些没有磁盘驱动器的系统使用(一般是无盘工作站或X终端),它需要系统管理员进行手工设置。
工作原理
ARP高速缓存
可以使用arp -a 命令查看
arp代理
为什么要有ARP代理?路由器的一个重要功能是把局域网广播包限制在该网内,不让扩散,否则会造成网络风暴。ARP Request是个广播包,它询问的对象若在同一个局域网内,就会回答。但如果查询对象不在同一个局域网,那怎么办呢?为了解决这个问题,路由器就提供一个服务:代理ARP.
代理arp的优缺点?
优点
最主要的一个优点就是能够在不影响其他router的路由表的情况下在网络上添加一个新的router,这样使得子网的变化对主机是透明的
proxy ARP应该使用在主机没有配置默认网关或没有任何路由策略的网络上
负面影响:
1.增加了某一网段上ARP流量
3.安全问题,比如ARP欺骗(spoofing)
5.不能够概括和 推广网络拓扑
免费arp
作用:
2.可以更新 其他主机的上对于该主机的arp缓存。(由于这个原因,如果有人伪造arp请求影响就很大,会影响整个局域网内的主机)。
RARP 的 头部格式基本上和ARP 没什么区别
诞生原因
工作原理
RARP服务器的复杂性
RARP服务器实现的一个复杂因素是RARP请求是在硬件层上进行广播的。这意味着它们不经过路由器进行转发。为了让无盘系统在RARP服务器关机的状态下也能引导,通常在一个网络上(例如一根电缆)要提供多个RARP服务器,这也加大了维护成本以及网络流量。
RARP请求是作为一个特殊类型的以太网数据帧来传送的,不同的系统会有不同的实现。
RARP的一个缺点就是它使用链路层广播
ICMP/因特网控制报文协议
ICMP 通常被认为是ip的附属协议,尽管他们是同一层的。
icmp数据包会有一个ip的首部,然后才是icmp的报文
左边是icmp报文的格式
ICMP 分为差错报文和查询报文,如下图
下面各种情况都不会导致产生ICMP差错报文:
1.ICMP差错报文(但是,ICMP查询报文可能会产生ICMP差错报文)。
3.作为链路层广播的数据报。
4.不是IP分片的第一片(将在11.5节介绍分片)。
这些规则是为了防止过去允许ICMP差错报文对广播分组响应所带来的广播风暴。
icmp 其他用途
ICMP时间戳请求允许系统向另一个系统查询当前的时间。返回的建议值是自午夜开始计算的毫秒数.
icmp和udp
UDP的规则之一是,如果收到一份UDP数据报而目的端口与某个正在使用的进程不相符,那么UDP返回一个ICMP不可达报文。注意,ICMP报文是在主机之间交换的,而不用目的端口号,而每个20字节的UDP数据报则是从一个特定端口(2924)发送到另一个特定端口(8888)。
ping 和 traceroute
ping he traceroute 都是基于icmp实现的应用程序。
ping 是利用 icmp的Echo Reply——回显应答)。即发送type+code=80 的icmp包也称echo request,对方接受到了就要回复类型 typecode 00的icmp包,也就是echo reply。
例如:
主机A 向主机Z发送数据包。
1 . 先送出一个TTL=1的包,下一跳路由B收到后TTL会减一变为0,TTL为0时路由器B将丢弃这个数据包并回复一个TTL超时的icmp包。
2.此时 主机A收到了 B的回复,知道 B肯定不是目的地。于是它再发一个TTL=2的数据包给目的地Z,经过路由器B后到了C手里时,正好TTL也为0,于是C又发一个TTL超时的icmp包。
3.此时主机A 收到了 C回复的icmp包,说明C也没到目的地。于是它再把TTL加一,现在发一个TTL=3的数据包。。。
4.最终,直到不会收到ICMP 的TTL超时回复。这说明包很有可能已经到了目的地。为了确认,它使用UDP向目标主机的一个明显无法到达巨大的端口号发送数据包,到了目的地后,目的主机Z会回复 端口不可达。
5.收到端口不可打后,主机A就可以确定包确实到了目的地,这样也就确定了AZ之间经过的路由了。
注意:一般来说,收到的数据包的TTL不会为0,但是的确会出现。如果路由器收到了了TTL=0的数据包,会进行TTL-1,最终得到的TTL为255(因为TTL字段是8位的),然后TTL会以255开始继续在网络上传播。
RTT:往返时延
IP选路
1. 路由表
1.U 该路由可以使用。
2.G 该路由是到一个网关(路由器)。如果没有设置该标志,说明目的地是直接相连的。
4.D 该路由是由重定向报文创建的。
5.M 该路由已被重定向报文修改。
标志G是非常重要的,因为由它区分了间接路由和直接路由(直接路不设置标志G)。
路由匹配规则
如果找不到,则会使用默认路由。
如果数据报是由本地主机产生的,那么就给发送该数据报的应用程序返回“主机不可达差错”或“网络不可达差错”。如果是被转发的数据报,就给原始发送端发送一份ICMP主机不可达的差错报文。
icmp 重定向差错
当IP数据报应该被发送到另一个路由器时,收到数据报的路由器就要发送ICMP重定向差错报文给IP数据报的发送端。例如,设A为源主机,B,C为网关/路由器,A经过 B发往C ,B发现A可以直接发给C,就会发起icmp重定向差错?那么 B如何得知 A可以直接连C 呢 ?
1.我们假定主机发送一份IP数据报给R1。这种选路决策经常发生,因为R1是该主机的默认路由。
2.R1收到数据报并且检查它的路由表,发现R2是发送该数据报的下一站。当它把数据报发送给R2时,R1检测到它正在发送的接口与数据报到达接口是相同的(即主机和两个路由器所在的LAN)。这样就给路由器发送重定向报文给原始发送端提供了线索。
3.R1发送一份ICMP重定向报文给主机,告诉它以后把数据报发送给R2而不是R1。
上面的例子和图片是卷一的原文,我的理解是(以ABC为例),B发数据给R2时,发现数据也正从同一网络接口流入。也就是说,BC作为网关在同一个子网,那么A也可以直接发给C,又因为B是A的默认路由,那么其实ABC在同一个子网内。
重定向差错的作用是在 主机没有完善的路由表时,可以通过重定向差错逐步建立起 完善的路由表
系统产生的或转发的每份IP数据报都要搜索路由表,它可以被路由守护程序或ICMP重定向报文修改。系统在默认情况下不转发数据报,除非进行特殊的配置。用route命令可以进入静态路由,可以利用新ICMP路由器发现报文来初始化默认表项,并进行动态修改。主机在启动时只有一个简单的路由表,它可以被来自默认路由器的ICMP重定向报文动态修改。
icmp 路由发现
路由器一般会在450-600秒的时间间隔内发布一次通告,而一个给定的通告报文的寿命是30分钟。而主机在引导的时候会每三秒发送一次请求报文,一旦接受到一个有效的通告报文,就停止发送请求报文。
ip选路 流程图
这张图是我自己根据理解的TCP/IP 协议 所画的,如有不合理的地方请帮忙指出。
动态选路协议
这一章真的看不懂
动态选路只是将 路由信息进行更新,ip选路的机制仍然没有变。
RIP:选路信息协议
先介绍RIP 协议
3.接收到响应。使响应生效,可能会更新路由表。可能会增加新表项,对已有的表项进行修改,或是将已有表项删除。
4.定期选路更新。每过30秒,所有或部分路由器会将其完整路由表发送给相邻路由器。发送路由表可以是广播形式的(如在以太网上),或是发送给点对点链路的其他终点的。
5.触发更新。每当一条路由的度量发生变化时,就对它进行更新。不需要发送完整路由表,而只需要发送那些发生变化的表项。
缺点
由于每个路由器要将自己整个路由表发送给所有相邻路由器,路由信息量大,占较大的网络开销;
好路由消息传播快,坏路由信息传播速度慢。网络出现故障,传播时间往往需要较长的时间(数分钟);
存在路由同步(我的计算结果依赖于你,你的计算结果依赖于我),算法收敛速度慢,容易引发更新不一致;
可扩展性不好,只能适用于小规模网络,一条路径上最多15个路由器;
可产生路由环路,为避免路由环路需要特殊处理;
RIP 中没有子网的概念
OSPF:开放最短路径优先
OSPF是除RIP外的另一个内部网关协议。它克服了RIP的所有限制。OSPF与RIP(以及其他选路协议)的不同点在于,OSPF直接使用IP。也就是说,它并不使用UDP或TCP
与采用距离向量的RIP协议不同的是,OSPF是一个链路状态协议。距离向量的意思是,RIP发送的报文包含一个距离向量(跳数)。每个路由器都根据它所接收到邻站的这些距离向量来更新自己的路由表。
UDP /用户数据报协议
首部
UDP是一个简单的面向数据报的无连接、不可靠的运输层协议。其工作原理只是简单的把应用程序发过来的包打上UDP自己8个字节的头部,并不考虑这个包的大小,然后直接扔给ip协议处理,由ip在包过大时进行分片等操作。
左图为封装格式,右图为 UDP的首部。 首部总共6字节。
UDP和TCP在首部中都有覆盖它们首部和数据的检验和。但是IP首部的检验和只覆盖IP的首部—并不覆盖IP数据报中的任何数据。
UDP数据报的特点
首先,UDP数据报的长度可以为奇数字节,但是检验和算法是把若干个16 bit字相加。解决方法是必要时在最后增加填充字节0,这只是为了检验和的计算(也就是说,可能增加的填充字节不被传送)。
如果接收端检测到检验和有差错,那么UDP数据报就会被悄悄地丢弃,不产生任何差错报文(当IP层检测到IP首部检验和有差错时也这样做)。
UDP检验和是一个端到端的检验和。它由发送端计算,然后由接收端验证。其目的是为了发现UDP首部和数据在发送端到接收端之间发生的任何改动。
UDP检验和是可选的
Host Requirements RFC 声明,UDP检验和选项在默认条件下是打开的。它还声明,如果发送端已经计算了检验和,那么接收端必须检验接收到的检验和(如接收到检验和不为0)。但是,许多系统没有遵守这一点,只是在出口检验和选项被打开时才验证接收到的检验和。
IP分片
物理网络层一般要限制每次发送数据帧的最大长度。任何时候IP层接收到一份要发送的IP数据报时,它要判断向本地哪个接口发送数据(选路),并查询该接口获得其MTU。IP把MTU与数据报长度进行比较,如果需要则进行分片。分片可以发生在原始发送端主机上,也可以发生在中间路由器上。
组装和再分片
把一份IP数据报分片以后,只有到达目的地才进行重新组装。重新组装由目的端的IP层来完成,其目的是使分片和重新组装过程对运输层(TCP和UDP)是透明的,除了某些可能的越级操作外。已经分片过的数据报有可能会再次进行分片(可能不止一次)。IP首部中包含的数据为分片和重新组装提供了足够的信息。
分片是可选的
标志字段中有一个比特称作“不分片”位。如果将这一比特置1,IP将不对数据报进行分片。相反把数据报丢弃并发送一个ICMP差错报文(“需要进行分片但设置了不分片比特”,大多数从Berkeley派生的实现从不产生该差错)
分片之间是独立的
当IP数据报被分片后,每一片都成为一个分组,具有自己的IP首部,并在选择路由时与其他分组独立。这样,当数据报的这些片到达目的端时有可能会失序,但是在IP首部中有足够的信息让接收端能正确组装这些数据报片。
分片的缺陷
IP分片过程看起来是透明的,但有一个严重的缺陷:即使只丢失一片数据也要重传整个数据报。因为IP层本身没有超时重传的机制——由更高层来负责超时和重传(TCP有超时和重传机制,但UDP没有。一些UDP应用程序本身也执行超时和重传)。对于TCP而言,当来自TCP报文段的某一片丢失后,TCP在超时后会重发整个TCP报文段,该报文段对应于一份IP数据报。没有办法只重传数据报中的一个数据报片。
广播组播(多播)单播
向网络内所有/特定分组(特定多个)/一个 主机 发送数据包
广播是将数据报发送到网络中的所有主机(通常是本地相连的网络),而多播是将数据报发送到网络的一个主机组。广播和多播区别在于当收到送往上一个协议栈的数据帧时采用不同类型的过滤。每个协议层均可以因为不同的理由丢弃数据报。
广播的含义是对网络上所有的主机发送包,因此其实也包括自己。在实现中,主机发送广播包时会给自己的环回口抄送一份。
广播会不会增加网络通信量?
广播本身不会增加网络通信量,但它增加了额外的主机处理时间。如果接收主机不正确地响应了诸如ICMP端口不可达之类的差错,那么广播也可能导致额外的网络通信量。
IGMP:Internet组管理协议
IGMP 用于支持主机和路由器进行多播,以避免广播带来的额外开销。
IGMPv1的工作机制可以分为:普遍组查询和响应机制、新成员加入机制和组成员离开机制三个方面。
正如ICMP一样,IGMP也被当作IP层的一部分。IGMP报文通过IP数据报进行传输。不像我们已经见到的其他协议,IGMP有固定的报文长度,没有可选数据。
IGMP协议经历了三个阶段的发展:
IGMPv1:
IGMPv1协议主要基于查询和响应机制完成组播组管理。需要选取其中一台组播路由器作为IGMP查询器不断发送查询报文。在IGMPv1中,由组播路由协议PIM选举出唯一的组播信息转发者(Assert Winner或DR)作为IGMPv1的查询器,负责该网段的组成员关系查询。
组成员离开机制:IGMPv1没有专门定义离开组的报文。主机离开组播组后,便不会再对普遍组查询报文做出回应。这里要区分两种情况:
1 . 分组内只有一台主机,这台主机退出了,IGMP收不到查询响应则认为这个组没有了。
2 . 分组内有多台主机,一台主机退出了,剩下的主机没有退出组,IGMP可以收到查询响应,会认为那台退出的主机仍然还在组内。
IGMPv2:
此外,与IGMPv1相比,IGMPv2的变化如下:
除了普遍组查询报文和成员报告报文之外,IGMPv2新增了两种报文:
成员离开报文(Leave):成员离开组播组时主动向查询器发送的报文,用于宣告自己离开了某个组播组。
特定组查询报文(Group-Specific Query):查询器向共享网段内指定组播组发送的查询报文,用于查询该组播组是否存在成员。
IGMPv2对普遍组查询报文格式也做了改进,添加了最大响应时间(Max Response Time)字段。此字段取值可以通过命令配置,用于控制成员对于查询报文的响应速度。
IGMPv3:
在工作机制上,与IGMPv2相比,IGMPv3增加了主机对组播源的选择能力。
如果主机和组播路由器之间运行的是IGMPv1或IGMPv2,主机加入组播组G时无法对组播源进行选择,无论其是否需要,都会同时接收到来自组播源S1和S2的数据。如果采用IGMPv3,成员主机可以选择仅接收S1组播数据。
DNS 域名系统
DNS 使用tcp还是UDP?
DNS一般情况下使用UDP通讯,但有两种情况例外:
1、当客户端发出DNS查询请求,从服务器收到的响应报文中的TC(删减标志)比特被置为1时,此时意味着服务器响应长度超过512字节,而仅返回前512字节。在遇到这种情况时,客户端会使用TCP重发起原来的DNS查询请求,它将运行返回的响应超过512字节。
2、DNS的主辅名字服务器在同步时使用TCP协议。辅名字服务器一般每3小时向主名字服务器发起查询,看主服务器是否有新的记录变动,如有变动,将执行一次区域传送,区域传送使用TCP协议。
TFTP:简单文件传送协议
TFTP(Trivial File Transfer Protocol)即简单文件传送协议,最初打算用于引导无盘系统(通常是工作站或X终端)。和使用TCP的文件传送协议(FTP)不同,为了保持简单和短小,TFTP将使用UDP。TFTP的代码(和它所需要的UDP、IP和设备驱动程序)都能适合只读存储器。
它只使用几种报文格式,是一种停止等待协议。
为了允许多个客户端同时进行系统引导,TFTP服务器必须提供一定形式的并发。因为UDP在一个客户与一个服务器之间并不提供唯一连接(TCP也一样),TFTP服务器通过为每个客户提供一个新的UDP端口来提供并发。这允许不同的客户输入数据报,然后由服务器中的UDP模块根据目的端口号进行区分,而不是由服务器本身来进行区分。
TFTP协议没有提供安全特性
BOOTP:引导程序协议
一个无盘系统被引导至少需要在只读存储器中拥有下列协议才能完成:BOOTP、TFTP、UDP、IP和一个局域网的驱动程序。
TCP传输控制协议
尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务。TCP提供一种面向连接的、可靠的字节流服务。
TCP提供了一种字节流服务,而收发双方都不保持记录的边界。应用程序如何提供它们自己的记录标识?
答:很多Internet应用使用一个回车和换行来标记每个应用记录的结束。这是NVT ASCII采用的编码(26.4节)。另外一种技术是在每个记录之前加上一个记录的字节计数,DNS(习题14.4)和Sun RPC(29.2节)采用了这种技术。
可靠性的实现
TCP通过以下手段实现可靠性:
1.建立连接的时候协商MSS(最大报文段) ,尽量避免ip分片。
2.超时重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
3.确认ACK机制:当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
4.校验和:TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。
5.序列号机制:TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序。TCP在建立连接时将会协商序列号,并按照序列号对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
6.既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
7.TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。
关于检验和
1.IP,ICMP,IGMP,UDP,TCP都有检验和,他们都是必须的吗?
答:UDP 内的检验和不是必须的。
2.为什么我们已经讨论的所有Internet协议(IP,ICMP,IGMP,UDP,TCP)收到有检验和错的分组都仅作丢弃处理而不是回复数据源错误原因呢?
TCP的数据报格式
TCP数据被封装在一个IP数据报中,如下图
图17-1 TCP数据在IP数据报中的封装
下图显示TCP首部的数据格式。如果不计任选字段,它通常是20个字节。ip+port 确定唯一一个TCP连接,也称socket。
为什么在TCP首部的开始便是源和目的的端口号?
一个ICMP差错报文必须至少返回引起差错的IP数据报中除了IP首部的前8个字节。当TCP收到一个ICMP差错报文时,它需要检查两个端口号以决定差错对应于哪个连接。因此,端口号必须包含在TCP首部的前8个字节里。
TCP的标志位
只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
序号
序号是32 bit的无符号数,序号到达232-1后又从0开始。
当建立一个新的连接时,SYN标志变1。序号字段包含由这个主机选择的该连接的初始序号ISN(Initial Sequence Number)。该主机要发送数据的第一个字节序号为这个ISN加1,因为SYN标志消耗了一个序号。
FIN标志也会占用一个序号。
TCP为应用层提供全双工服务。这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。
确认ACK
确认序号包含发送确认的一端所期望收到的下一个序号。
这里的意思是,当一端收到了序列号1-1000 的数据,它回复的确认序号应该为1001,表示下一次希望接受从1001开始,这样也侧面说明了,前1000个是一定收到了。就不必要每个序号都回复一个确认。
发送ACK无需任何代价,因为32 bit的确认序号字段和ACK标志一样,总是TCP首部的一部分。因此,一旦一个连接建立起来,这个字段总是被设置,ACK标志也总是被设置为1。
TCP可以表述为一个没有选择确认或否认的滑动窗口协议。
说TCP缺少选择确认是因为TCP首部中的确认序号表示发方已成功收到前多少个字节,严格按照顺序进行确认,不能确认已收到的不连续的序号。例如,如果1~1024字节已经成功收到,TCP必须只回复希望收到1025,即使此时 它收到了 2000后的所有序号,也只能回复希望收到从1025开始的包,没办法确认和之前收到的序号不连续的包。
同样,它也无法对一个报文段进行否认。例如,如果收到包含1025~2048字节的报文段,但它的检验和是错的,TCP接收端所能做的仍然只是发回一个确认序号为1025的ACK,表示希望收到序号为1025开始的包,没办法通知你是否是没接收到或者是因为检验和错。
TCP连接的建立与终止
建立过程(三次握手):
以下图为例:
1.请求端(通常称为客户)发送一个SYN段指明客户打算连接的服务器的端口,以及初始序号(ISN,在这个例子中为1415531521)。这个SYN段为报文段1。
2.服务器发回包含服务器的初始序号的SYN报文段(报文段2)作为应答。同时,将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号。
3.客户必须将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认(报文段3)。
发送第一个SYN的一端将执行主动打开(active open)。接收这个SYN并发回下一个SYN的另一端执行被动打开(passive open)
为什么建立连接要送三次握手?两次不行吗
首先,由于TCP连接是全双工的,在连接时需要确认双向通道是否都是可用。如果两次握手就建立连接,很容 易因为第二次握手时发送的回复确认数据包没有正确到达对方而造成双方都在等待,形成死锁。
其次,三次握手可以避免由于已经失效的连接请求报文突然又传送到了服务器而造成的错误。
因为网络的不确定性,我们假设当有一个TCP客户端发出的连接请求没有丢失,只是因为在网络结点中滞留的时间过长,而由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,会重新向服务器发送这条报文,如果此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。而恰好之前滞留在网络路由的那一次连接请求,此时刚刚到达服务器。这个报文本该是算作是失效的,但是,两次握手的机制将会让服务器打开连接,这将导致不必要的错误和资源的浪费。而采用三次握手的话,即使服务器收到了无效的数据包,并且发送一个连接请求给客户端,但是客户端并不进行理睬,服务器就会知道这次连接是无效的。
关闭过程(四次挥手)
1.主动关闭方发送FIN包,序列号为x
2.被动关闭方收到后,回复一个序列号1为X+1的ack包。
3.被动关闭方也发送一个FIN包,序列号为Y
4.主动关闭方收到后回复一个Y+1的ack
建立连接需要三次,关闭却需要四次,为什么?
这是由TCP的半关闭(halfclose)造成的。既然一个TCP连接是全双工(即数据在两个方向上能同时传递),因此每个方向必须单独地进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向连接。收到一个FIN只意味着在这一方向上没有数据流动。一个TCP连接在收到一个FIN后仍能发送数据。而这对利用半关闭的应用来说是可能的,尽管在实际应用中只有很少的TCP应用程序这样做。
第一次超时时间
这里要讲的是,第一次超时时间往往不是整数秒。但是第二次第三次都是精确地整数秒,为什么会这样?
答:在这是因为BSD版的TCP软件采用一种500 ms的定时器。这种500 ms的定时器用于确定本章中所有的各种各样的TCP超时。开始尝试连接时,将建立一个6秒的定时器(12个时钟滴答(tick)),但它可能在之后的5.5秒~6秒内的任意时刻超时。尽管定时器初始化为12个时钟滴答,但定时计数器会在设置后的第一个0~500 ms中的任意时秒刻减1。所以第一个超时时间往往不是整数秒。但是过了第一个超时时间后,定时器会复位。之后的下一个定时器将更接近24秒。
MSS最大报文长度
最大报文段长度(MSS)表示一个TCP连接可传输的最大数据块的长度。当一个连接建立时,连接的双方都要通告各自的MSS。在有些书中,将它看作可“协商”选项。它并不是任何条件下都可协商。如果一方不接收来自另一方的MSS值,则MSS就定为默认值536字节
TCP的半关闭
因为TCP是全双工的,TCP的半关闭指的是执行关闭过程中,一个方向的数据流动停止,另一方向没有停止。
为什么会需要有这么一个机制?
没有半关闭,需要其他的一些技术让客户通知服务器,客户端已经完成了它的数据传送,但仍要接收来自服务器的数据。使用两个TCP连接也可作为一个选择,但使用半关闭的单连接更好。
以RSH 程序为例,RSH是把客户端的输入复制到服务端,服务端执行输入并将输出传给客户端。
当客户端执行完发送时,通过执行主动关闭发送FIN,服务端就知道客户端发送完毕,可以开始执行输入了。
状态变迁图
为什么要等待2MSL
MSL 是Maximum Segment Lifetime 最大报文存活时间,2MSL 恰好意味着报文来回一趟的最大时间。
第一,为了保证主动关闭方的最后一个ACK报文能够到达被动关闭方。这个ACK报文段有可能丢失,如果主动方发送完ACK报文段后就立即关闭连接,而最后一个ACK报文又未按时到达被动方,被动方就无法正常关闭。当设立等待2MSL 时间, 被动方如果没收到最后的ACK报文,会超时重传自己的上一个FIN+ACK报文段,而主动方就能在2MSL时间内收到这个重传的FIN+ACK报文段,并也进行重传最后的ACK报文并重新计时2MSL,这样,被动方才能更可靠的正常关闭连接。
第二,A在发送完ACK报文段后,再经过2MSL时间,就可以使本连接持续的时间所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求的报文段。
复位报文段
TCP首部中的RST比特是用于“复位”的。一般说来,无论何时一个报文段发往基准的连接(referenced connection)出现错误,TCP都会发出一个复位报文段
复位报文的三种场景:
产生复位的一种常见情况是当连接请求到达时,目的端口没有进程正在听。对于UDP,我们在6.5节看到这种情况,当一个数据报到达目的端口时,该端口没在使用,它将产生一个ICMP端口不可达的信息。而TCP则使用复位。
用来实现连接的异常释放:终止一个连接的正常方式是一方发送FIN。有时这也称为有序释放(orderly release),因为在所有排队数据都已发送之后才发送FIN,正常情况下没有任何数据丢失。但也有可能发送一个复位报文段而不是FIN来中途释放一个连接。有时称这为异常释放(abortive release)。
异常终止一个连接对应用程序来说有两个优点:(1)丢弃任何待发数据并立即发送复位报文段;(2)RST的接收方会区分另一端执行的是异常关闭还是正常关闭。应用程序使用的API必须提供产生异常关闭而不是正常关闭的手段。
用来检测半打开状态
如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的TCP连接称为半打开(Half-Open)的。任何一端的主机异常都可能导致发生这种情况。只要不打算在半打开连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。
同时打开
两个应用程序同时彼此执行主动打开的情况是可能的,尽管发生的可能性极小。每一方必须发送一个SYN,且这些SYN必须传递给对方。这需要每一方使用一个对方熟知的端口作为本地端口。这又称为同时打开(simultaneous open)。
TCP是特意设计为了可以处理同时打开,对于同时打开它仅建立一条连接而不是两条连接
同时关闭
TCP协议也允许同时关闭(simultaneous close)。
图18-19 同时关闭期间的报文段交换
同时关闭与正常关闭使用的段交换数目相同
如何实现连接的并发?
在伯克利的TCP实现中正等待连接请求的一端有一个固定长度的连接队列,该队列中的连接已被TCP接受(即三次握手已经完成),但还没有被应用层所接受。
注意区分TCP接受一个连接是将其放入这个队列,而应用层接受连接是将其从该队列中移出
应用层将指明该队列的最大长度,这个值通常称为积压值(backlog)。它的取值范围是0~5之间的整数,包括0和5。
当一个连接请求(即SYN)到达时,TCP使用一个算法,根据当前连接队列中的连接数来确定是否接收这个连接。
如果对于新的连接请求,连接队列中已没有空间,TCP将不理会收到的SYN。也不发回任何报文段(即不发回RST)。如果应用层不能及时接受已被TCP接受的连接,这些连接可能占满整个连接队列,客户的主动打开最终将超时。
TCP连接的建立对应用程序是透明的
TCP的 成块数据流和交互数据流
交互数据流:主要应用场景为交互性/实时性场景的数据流,特点是会产生很多小分组报文。
成块数据流则是和交互数据流对应,大小是固定的,并不是用于实时交互的。比如邮件,新闻,文件等。
交互数据流
关于交互数据流,例如使用Telnet、Rlogin远程登录,每敲击一个键就会传输一个字符,这个过程会产生四次数据交互,假设我们在本地机器敲击 q 键
1、客户端产生一个41bit长的报文(20字节的IP首部,20字节的TCP首部,1字节的数据),发送到服务端;
2、服务端发送过来一个40bit的确认报文;
3、服务端发送回显的字符,报文长为41bit;
4、客户端发送确认报文,报文长为40bit。
可以看到这些小分组报文中TCP和IP头部就占了40字节,真正用于进程的有效数据报文长度非常小,这意味着大量的报文头部将会占用相当多的网络资源。如果在局域网中,很多交互数据流通常不会有什么麻烦,因为局域网一般不会出现拥塞,但在广域网中,这些小分组则会增加网络拥塞出现的可能。
为了提高这类数据的发送效率和降低网络负担,TCP采用了两种策略:捎带ACK和Nagle算法。但要注意的是,Nagle不是必须的,可以禁用。
捎带确认/延迟确认
通常TCP在接收到数据时并不立即发送ACK;相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送(有时称这种现象为数据捎带ACK)。绝大多数实现采用的时延为200 ms,也就是说,TCP将以最大200 ms的时延等待是否有数据一起发送。
经过捎带确认,可以有效减少 网络中的小分组包的个数。
Nagle 算法
该算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。
Nagle算法的规则(可参考tcp_output.c文件里tcp_nagle_check函数注释):
(1)如果包长度达到MSS,则允许发送;
(2)如果该包为紧急数据或包含FIN(即关闭连接的字段),则允许发送;
(3)设置了TCP_NODELAY选项,则允许发送;
(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
(5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。
不满足上述发送条件的小分组会和发送缓冲区中新来的分组打包在一起,直到满足发送条件并发出。
在跨广域网运行一个交互应用的环境下,当进行多字节的按键输入时,默认使用Nagle算法会引起额外的时延。Nagle 是可选的,可以被关闭。
成块数据流
在成块数据流里,主要讲的是通过滑动窗口避免像停等协议一样确认频繁等待,提高传输效率。
滑动窗口
工作原理:
在任意时刻,发送方和接收方都各自维持了允许发送的帧序号范围,称为窗口;发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的的序号分为两类:已发送但未收到确认;未发送但可以被发送。
左图为发送窗口。
在通信的过程中,接收方并不会每个包都回复ack,而是向我们讲交互数据流中的一样,是延迟发送的。在发送一个ACK前不必等待窗口被填满。并且在回复累计的ack时,接收方还会一并通知发送方接收方的空余窗口大小。发送方收到确认后,会按照接收方发来的窗口大小和ack序号去发送连续多个包。
窗口大小有默认值,一般是4096字节,但是进程可以控制。
滑动窗口可以有效提升通信效率(允许发送端发送多个并且接收方累计ACK),也是一种有效的流量控制手段,以窗口大小限制了发送方发送的速率,避免造成缓冲区溢出和网络拥塞。
慢启动
上文讲到了滑动窗口,讲的是通信过程已经开始。但是没有提到如何开始。滑动窗口在局域网内表现是很好的,但是在跨网络中的仍然存在一些问题。在跨网络通信的过程中,中间的路由器可能会缓存tcp的数据包分组。不断在消耗存储空间直到产生拥塞。 因此在通信过程启动时,发送方应该采取谨慎的态度开始第一步。
TCP 慢启动指的是在启动时,设一个可连续发送包的最大值N,初始值为1。之后每次接收到一个ack,N就加1。其实这样一点也不慢,是指数级别的增长。仍然会导致网络拥塞,只不过是避免了刚启动就拥塞的情况。
关于拥塞控制下文会继续更详细的讲述。
紧急数据
在许多传输层中有带外数据的概念,即此数据优先级较高,且不应和普通数据使用同样的传输通道。通常只会在缓冲区中分配一个区域存放特定的高优先级的数据,并不会单独新建连接。TCP中并没有真正的带外数据,但是提供了一种紧急模式的机制,即紧急数据。该机制会确保即便数据的流动会因为TCP的流量控制而停止,紧急数据却总是无障碍地发送到对端。
注意:UDP未实现此机制
TCP的超时和重传
对于每个连接,TCP管理4个不同的定时器。
1.重传定时器使用于当希望收到另一端的确认。在本章我们将详细讨论这个定时器以及一些相关的问题,如拥塞避免。
2.坚持(persist)定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口。第22章将讨论这个问题。
3.保活(keepalive)定时器可检测到一个空闲连接的另一端何时崩溃或重启。第23章将描述这个定时器。
4.2MSL定时器测量一个连接处于TIME_WA IT状态的时间。我们在18.6节对该状态进行了介绍。
重传的指数退避:
发生超时时,重传的间隔按照以第一次超时时间为基础,进行2的X次方秒进行递增。
如第一次计算得出1.5秒超时,下一次则规定3(1.5*2)秒后超时,再下一次是6(1.5*2^2),12(1.5*2^3)...
对单个节点来说,指数退避是最快让每个节点都正常工作的方案。但是对于两台特定主机之间,按固定间隔会更合适。TCP看的角度更高。
重传不一定要发送和上一次相同的包,重传时仍然遵循窗口协议。发送的包的大小随窗口大小而定。
两个关键问题:
往返时间(RTT)测量:
首先TCP必须测量在发送一个带有特别序号的字节和接收到包含该字节的确认之间的RTT 之后使用一些特定的算法得到一个初始值,并且RTT会在通信过程中随着通信质量的改变而波动。
重传的多义性:
发生超时重传后,如何确定收到的ack是第一次发送的分组的ack还是重传分组的ack?
由于数据被重传,RTO(超时定时器)已经得到了一个指数退避,我们在下一次传输时使用这个退避后的RTO。对一个没有被重传的报文段而言,除非收到了一个确认,否则不要计算新的RTO。
TCP 中的拥塞控制机制?
拥塞机制是为了防止在时间段内向网络发送太多数据包导致网络路由或链路过载,导致阻塞、丢包等问题。
TCP的拥塞机制主要分为四个部分:慢开始 、 拥塞避免、快重传 、 快恢复 。
慢开始 : 在建立连接后,由于不清楚网络的负载,如果一开始就发送大量数据包极易造成阻塞。更合适的做法是由小到大逐步试探可以发送的拥塞窗口cwnd的大小,每收到一个ACK,cwnd加一。.不同TCP版本的实现中慢开始的初始值还不同,这里我们以 cwnd=1( 1个最大报文短长度 MSS ) 开始。
第一轮: cwnd=1 发送一个数据 , 收到一个ACK返回
第二轮 :cwnd =1+1=2 发送两个数据, 之后收到两个ACK
第三轮 :cwnd=2+2=4 发送四个数据,之后收到 四个ACK
第四轮 :cwnd =4+4=8
..........
可以看出慢开始窗口增长速率是以 2**n 的指数级别进行增长, 慢开始的慢并不是指发送窗口增长速率慢,而是单单指初始值很小,很慢。
于指数级别的增长实在太快,到最后几乎无法避免阻塞。因此,还需要设置一个阈值 ssthresh (默认是16)来 防止窗口增 长过快。并且仅当窗口小于此阈值的时候才允许以慢开始的方式进行发包。 当拥塞窗口大于此阈值时,TCP就会 启用另一种算法:拥塞避免
拥塞避免:
拥塞避免并不是指完全避免拥塞,更像是指让避免拥塞来得太快。其核心思想是让拥塞窗口cwnd缓慢的增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1 。
经过慢开始和拥塞避免两个算法,窗口还是在增大,只是增大速率变慢了,迟早还是会到达阻塞。
无论是在哪个阶段发生了阻塞(出现超时或重复确认),ssthresh 都要变为拥塞前的一半,而窗口则又从1 开始,即又进行慢开始算法。
在发生阻塞时,接收方由于缓存溢出会产生丢包,会产生失序报文。TCP的重传定时器会在某个数据包在规定时间内未收到ACK而进行重传,这导致确认阻塞的过程将比较耗时。为了解决这个问题,TCP又引入快速重传和快速恢复机制。
快速重传
背景:
由于TCP使用ip进行数据传输,ip包是无序的。那么如果发送方收到了一个ACK,其实无法确定这个序号的包是被收到后丢弃,还是因为未能按序到达但是还在路上。
快速重传规定接收方如果发现报文失序,需要立即连续发送三次重复确认,且无需等待捎带和计时。
发送方当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。
为什么是连续三次?
这里是做了一个权衡,连续收到三次重复ack 更有可能是因为丢失导致。没有必要再等下去了。
这里是一个权衡。
发送方一连收到三个重复确认后判定网络可能发生阻塞,因为失序报文也可能是单纯地在网络路由中传送太久,并未发生阻塞。
快速恢复
收到三个重复确认后,网络只是可能阻塞,为了尽量不降低网络传输效率,不应该立即从cwnd=1开始执行慢开始。快速恢复采用将ssthresh 和cwnd设为原先拥塞窗口的一半,并开始执行拥塞避免,这样就实现了快速恢复的效果。
TCP的坚持定时器
如果一个确认丢失了,则双方就有可能因为等待对方而使连接终止:接收方等待接收数据(因为它已经向发送方通告了一个非0的窗口),而发送方在等待允许它继续发送数据的窗口更新。为防止这种死锁情况的发生,发送方使用一个坚持定时器(persist timer)来周期性地向接收方查询,以便发现窗口是否已增大。
糊涂窗口综合征
指的是接收方窗口可用大小很小,比如1个字节,但是接收方仍然如实回复给了发送方,发送方也耿直地只发一个字节的包。这会导致网络上开始充斥着很多极小的数据包。
解决方案:
如果糊涂窗口是发送方造成的,发送端采用Nagle算法即可。
如果是接收端造成
一:只要有数据到达就发送确认,但宣布的窗口大小为零,直到或者缓存空间已能放入具有最大长度的报文段,或者缓存空间的一半已经空了。
二:延迟确认。(有可能会导致发送方超时重传)
TCP的保活定时器
如果一个给定的连接在两个小时之内没有任何动作,则服务器(设置了保活选项的一方)就向客户发送一个探查报文段(我们将在随后的例子中看到这个探查报文段看起来像什么)。客户主机必须处于以下4个状态之一。
1.客户主机依然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常工作的。服务器在两小时以后将保活定时器复位。如果在两个小时定时器到时间之前有应用程序的通信量通过此连接,则定时器在交换数据后的未来2小时再复位。
2.客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的TCP都没有响应。服务器将不能够收到对探查的响应,并在75秒后超时。服务器总共发送10个这样的探查,每个间隔75秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
3.客户主机崩溃并已经重新启动。这时服务器将收到一个对其保活探查的响应,但是这个响应是一个复位,使得服务器终止这个连接。
4.客户主机正常运行,但是从服务器不可达。这与状态2相同,因为TCP不能够区分状态4与状态2之间的区别,它所能发现的就是没有收到探查的响应。