TCP/IP详解——链路层
以太网的链路层协议:两个串行接口链路层协议(SLIP 和PPP), 以及大多数实现都包含的环回(loopback)驱动程序。 MTU: 最大传输单元2.2. 以太网和IEEE 802封装 ——我们常说的都是以太网的封装格式(常用)以 太 网 这 个 术 语 一 般 是 指 数 字 设 备 公 司 ( D i g i t a l E q u i p m e n t C o r p . )、 英 特 尔 公 司 ( I n t e l C o r p . )和 X e r o x 公司在 1 9 8 2 年 联 合 公 布 的 一 个 标 准 。8 0 2 . 3 针对整个 C S M A / C D 网络,8 0 2 . 4 针 对 令 牌 总 线 网 络 ,8 0 2 . 5 针 对 令 牌 环 网 络 。都是由 8 0 2 . 2标 准 来 定 义 , 那 就 是 8 0 2 网 络 共 有 的 逻 辑 链 路 控 制 ( L L C )。 不 幸 的 是 , 8 0 2 . 2 和 8 0 2 . 3 定 义 了 一 个 与 以 太 网 不 同 的 帧 格 式 。IEEE 802要求每台Internet主机都与一个10Mb/s的以太网电缆相连接的:1)必须能发送和接收采用REC 1042(IEEE 802)封装格式的分组2)应该能够接收与RFC 894 混合的REC 1042封装格式的分组3)也许能够发送采用 RFC 1042格式封装的分组。如果主机能同时发送两种类型的分组数据,那么发送的分组必须是可以设置的,而且默认条件下必须是 RFC 894分组。RFC 894 和 RFC 1042两种帧格式都采用 4 8 b i t ( 6 字节)的目的地址和源地址( 8 0 2 . 3 允许使用 1 6 b i t 的地址,但一般是 4 8 b i t 地址)。这就是我们在本书中所称的硬件地址。 A R P 和 R A R P 协议(第4 章和第 5 章) 对 3 2 b i t 的 I P 地址和 4 8 b i t 的 硬 件 地 址 进 行 映 射 。C R C 字 段 用 于 帧 内 后 续 字 节 差 错 的 循 环 冗 余 码 检 验 ( 检 验 和 )( 它 也 被 称 为 F C S 或帧检验序列)。 —— 这个需要看一下这个是怎么校验的2.3 尾部封装描述了另一种用于以太网的封装格式,称为:尾部封装(trailer encapsulation)通过调整IP数据包中字段的次序来提高性能。《在以太网中数据帧中,开始的那部分是边长的字段(IP首部和TCP首部)》把它们移到尾部(在CRC之前),这样当把数据复制到内核时,就可以把数据帧中的数据部分映射到一个硬件页面, 节省内存到内存的复制过程。TCP 数据报的长度是512字节的整数倍,正好可以用内核中的页表处理。 —— 所以,我们要了解内存的分页过程—— 现在基本上是反对了尾部封装了; (可以略过)2.4 SLIP: 串行线路IP(Serial Line IP)它是一种在串行线路上对IP数据报进行封装的简单形式。SLIP适用于家庭中每台计算机几乎都有的RS-232串行端口和高速调制解调器接入Internet。SLIP 缺陷:1)每一端必须知道对方的 I P 地址。没有办法把本端的 I P 地址通知给另一端。2)数 据 帧 中 没 有 类 型 字 段 ( 类 似 于 以 太 网 中 的 类 型 字 段 )。如果一条串行线路用于 S L I P ,那么它不能同时使用其他协议3)S L I P 没 有 在 数 据 帧 中 加 上 检 验 和 ( 类 似 于 以 太 网 中 的 C R C 字段)。如果 S L I P 传 输 的 报 文被线路噪声影响而发生错误,只能通过上层协议来发现(另一种方法是,新型的调制解调 器 可 以 检 测 并 纠 正 错 误 报 文 )现在很多厂家都支持这个协议;2.5 压缩的SLIP由 于 串 行 线 路 的 速 率 通 常 较 低 ( 1 9 2 0 0 b / s 或 更 低 ), 而 且 通 信 经 常 是 交 互 式 的 ( 如 T e l n e t 和 R l o g i n , 二 者 都 使 用 T C P ),因此在 S L I P 线 路 上 有 许 多 小 的 T C P 分 组 进 行 交 换 。 为 了 传 送 1 个 字 节 的 数 据 需 要 2 0 个字节的 I P 首部和 2 0 个字节的 T C P 首 部 , 总 数 超 过 4 0 个字节;C S L I P 一般能把上面的 4 0 个字节压缩到 3 或 5 个 字节。2.6 PPP 点对点协议修改了SLIP协议的所有缺陷包括了三个部分:1)在串行链路上封装IP数据包的方法。支持数据为8bit和无奇偶校验的异步模式,还支持面向比特的同步链接2)建立、配置及测试数据链路的链路控制协议(TCP:Link Control Protocol) 。 它允许通信双方进行协商,已确定不同的选项。3)针对不同的网络层协议的网络控制协议(NCP:Network Control Protocol)体系。当前RFC定义的网络层有IP、OSI网络层、DECnet以及AppleTalk。每一帧都以标志字符 0 x 7 e 开始和结束。紧接着是一个地址字节,值始终是 0 x ff ,然后是一 个值为 0 x 0 3 的控制字节。信息中如果有0x7E , 那么就需要采用比特填充(bit stuffing)的硬件技术来完成的;2.7 环回接口 (lookback interface)允许运行在同一台主机上的客户程序和服务程序通过TCP/IP 进行通信。A类网络号127就是给环回接口预留的;一般系统把IP地址127.0.0.1 分配给这个接口,并命名为localhost。 一个传给环回的IP数据包不能在任何网络上出现。检测到目的端地址是环回地址时,应该可以省略部分传输层和所 有网络层的逻辑操作。但是大多数的产品还是照样完成传输层和网络层的所有过程,只是当I P 数据报离开网络层时把它返回给自己。2.8 最大传输单元MTU链路上对数据帧的长度都有一个限制的特性 —— MTU如果 I P 层 有 一 个 数 据 报 要 传 , 而 且 数 据的长度比链路层的 M T U 还大,那么 I P 层 就 需 要 进 行 分 片 ( f r a g m e n t a t i o n ), 把 数 据 报分成若干片,这样每一片都小于 M T U 。点到点的链路层(如 S L I P 和 P P P )的 M T U并非指的 是网络媒体的物理特性。相反,它是一个逻辑限制,目的是为交互使用提供足够快的响应时 间。2.9 路径MTU当在同一个网络上的两台主机互相进行通信时,该网络的 M T U 是 非 常 重 要 的 。但 是 如 果 两台主机之间的通信要通过多个网络,那么每个网络的链路层就可能有不同的 M T U 。重要的不是两台主机所在网络的 M T U的值,重要的是两台通信主机路径中的最小 M T U 。它被称作路径M T U。两台主机之间的路径 M T U 不 一 定 是 个 常 数 。 它 取 决 于 当 时 所 选 择 的 路 由 。 而 选 路 不 一 定 是 对 称 的 ( 从 A 到 B 的 路 由 可 能 与 从 B 到 A 的 路 由 不 同 ), 因 此 路 径 M T U 在 两 个 方 向 上 不 一 定 是 一致的。——> 动态、方向2.10 串行线路吞吐量计算将用这些串行线路吞吐量的计算来验证数据从串行线路上通过的 时间。数据块的划分: 考虑到数据的占用比例,和等待的时间问题,取一个平衡的值; 。。。。 这个需要进行计算

TCP-IP协议详解(3) IP/ARP/RIP/BGP协议
网络层(network layer)是实现互联网的最重要的一层。正是在网络层面上,各个局域网根据IP协议相互连接,最终构成覆盖全球的Internet。更高层的协议,无论是TCP还是UDP,必须通过网络层的IP数据包(datagram)来传递信息。操作系统也会提供该层的socket,从而允许用户直接操作IP包。 IP数据包是符合IP协议的信息(也就是0/1序列),我们后面简称IP数据包为IP包。IP包分为头部(header)和数据(Data)两部分。数据部分是要传送的信息,头部是为了能够实现传输而附加的信息(这与以太网帧的头部功能相类似,如果对帧感到陌生,可参看 小喇叭 一文)。IP协议可以分为IPv4和IPv6两种。IPv6是改进版本,用于在未来取代IPv4协议。出于本文的目的,我们可以暂时忽略两者的区别,只以IPv4为例。下面是IPv4的格式IPv4包 我们按照4 bytes将整个序列折叠,以便更好的显示与帧类似,IP包的头部也有多个区域。我们将注意力放在红色的发出地(source address)和目的地(destination address)。它们都是IP地址。IPv4的地址为4 bytes的长度(也就是32位)。我们通常将IPv4的地址分为四个十进制的数,每个数的范围为0-255,比如192.0.0.1就是一个IP地址。填写在IP包头部的是该地址的二进制形式。IP地址是全球地址,它可以识别”社区”(局域网)和”房子”(主机)。这是通过将IP地址分类实现的。IP class From To Subnet MaskA 1.0.0.0 126.255.255.255 255.0.0.0B 128.0.0.0 191.255.255.255 255.255.0.0C 192.0.0.0 223.255.255.255 255.255.255.0每个IP地址的32位分为前后两部分,第一部分用来区分局域网,第二个部分用来区分该局域网的主机。子网掩码(Subnet Mask)告诉我们这两部分的分界线,比如255.0.0.0(也就是8个1和24个0)表示前8位用于区分局域网,后24位用于区分主机。由于A、B、C分类是已经规定好的,所以当一个IP地址属于B类范围时,我们就知道它的前16位和后16位分别表示局域网和主机。网络协议概览 中说,IP地址是分配给每个房子(计算机)的“邮编”。但这个说法并不精确。IP地址实际上识别的是网卡(NIC, Network Interface Card)。网卡是计算机的一个硬件,它在接收到网路信息之后,将信息交给计算机(处理器/内存)。当计算机需要发送信息的时候,也要通过网卡发送。一台计算机可以有不只一个网卡,比如笔记本就有一个以太网卡和一个WiFi网卡。计算机在接收或者发送信息的时候,要先决定想要通过哪个网卡。NIC路由器(router)实际上就是一台配备有多个网卡的专用电脑。它让网卡接入到不同的网络中,这样,就构成在 网络协议概览 中所说的邮局。比如下图中位于中间位置的路由器有两个网卡,地址分别为199.165.145.17和199.165.146.3。它们分别接入到两个网络:199.165.145和199.165.146。IP包的传输要通过路由器的接力。每一个主机和路由中都存有一个路由表(routing table)。路由表根据目的地的IP地址,规定了等待发送的IP包所应该走的路线。就好像下图的路标,如果地址是“东京”,那么请转左;如果地址是“悉尼”,那么请向右。A real world routing table比如我们从主机145.17生成发送到146.21的IP包:铺开信纸,写好信的开头(剩下数据部分可以是TCP包,可以是UDP包,也可以是任意乱写的字,我们暂时不关心),注明目的地IP地址(199.165.146.21)和发出地IP地址(199.165.145.17)。主机145.17随后参照自己的routing table,里面有三行记录:145.17 routing table (Genmask为子网掩码,Iface用于说明使用哪个网卡接口)Destination Gateway Genmask Iface199.165.145.0 0.0.0.0 255.255.255.0 eth00.0.0.0 199.165.145.17 0.0.0.0 eth0这里有两行记录。第一行表示,如果IP目的地是199.165.145.0这个网络的主机,那么只需要自己在eth0上的网卡直接传送(“本地社区”:直接送达),不需要前往router(Gateway 0.0.0.0 = “本地送信”)。第二行表示所有不符合第一行的IP目的地,都应该送往Gateway 199.165.145.17,也就是中间router接入在eth0的网卡IP地址(邮局在eth0的分支)。我们的IP包目的地为199.165.146.21,不符合第一行,所以按照第二行,发送到中间的router。主机145.17会将IP包放入帧的payload,并在帧的头部写上199.165.145.17对应的MAC地址,这样,就可以按照 以太网与wifi协议 中的方法在局域网中传送了。中间的router在收到IP包之后(实际上是收到以太协议的帧,然后从帧中的payload读取IP包),提取目的地IP地址,然后对照自己的routing table:Destination Gateway Genmask Iface199.165.145.0 0.0.0.0 255.255.255.0 eth0199.165.146.0 0.0.0.0 255.255.255.0 eth10.0.0.0 199.165.146.8 0.0.0.0 eth1从前两行我们看到,由于router横跨eth0和eth1两个网络,它可以直接通过eth0和eth1上的网卡直接传送IP包。第三行表示,如果是前面两行之外的IP地址,则需要通过eth1,送往199.165.146.8(右边的router)。我们的目的地符合第二行,所以将IP放入一个新的帧中,在帧的头部写上199.165.146.21的MAC地址,直接发往主机146.21。(在Linux下,可以使用$route -n来查看routing table)IP包可以进一步接力,到达更远的主机。IP包从主机出发,根据沿途路由器的routing table指导,在router间接力。IP包最终到达某个router,这个router与目标主机位于一个局域网中,可以直接建立连接层的通信。最后,IP包被送到目标主机。这样一个过程叫做routing(我们就叫IP包接力好了,路由这个词实在是混合了太多的意思)。整个过程中,IP包不断被主机和路由封装入帧(信封)并拆开,然后借助连接层,在局域网的各个NIC之间传送帧。整个过程中,我们的IP包的内容保持完整,没有发生变化。最终的效果是一个IP包从一个主机传送到另一个主机。利用IP包,我们不需要去操心底层(比如连接层)发生了什么。在上面的过程中,我们实际上假设了,每一台主机和路由都能了解局域网内的IP地址和MAC地址的对应关系,这是实现IP包封装(encapsulation)到帧的基本条件。IP地址与MAC地址的对应是通过ARP协议传播到局域网的每个主机和路由。每一台主机或路由中都有一个ARP cache,用以存储局域网内IP地址和MAC地址如何对应。ARP协议(ARP介于连接层和网络层之间,ARP包需要包裹在一个帧中)的工作方式如下:主机会发出一个ARP包,该ARP包中包含有自己的IP地址和MAC地址。通过ARP包,主机以广播的形式询问局域网上所有的主机和路由:我是IP地址xxxx,我的MAC地址是xxxx,有人知道199.165.146.4的MAC地址吗?拥有该IP地址的主机会回复发出请求的主机:哦,我知道,这个IP地址属于我的一个NIC,它的MAC地址是xxxxxx。由于发送ARP请求的主机采取的是广播形式,并附带有自己的IP地址和MAC地址,其他的主机和路由会同时检查自己的ARP cache,如果不符合,则更新自己的ARP cache。这样,经过几次ARP请求之后,ARP cache会达到稳定。如果局域网上设备发生变动,ARP重复上面过程。(在Linux下,可以使用$arp命令来查看ARP的过程。ARP协议只用于IPv4。IPv6使用Neighbor Discovery Protocol来替代ARP的功能。)我们还有另一个假设,就是每个主机和路由上都已经有了合理的routing table。这个routint table描述了网络的拓扑(topology)结构。如果你了解自己的网络连接,可以手写自己主机的routing table。但是,一个路由器可能有多个出口,所以routing table可能会很长。更重要的是,周围连接的其他路由器可能发生变动(比如新增路由器或者路由器坏掉),我们就需要routing table能及时将交通导向其他的出口。我们需要一种更加智能的探测周围的网络拓扑结构,并自动生成routing table。我们以北京地铁为例子。如果从机场前往朝阳门,那么可以采取2号航站楼->>三元桥->>东直门->>朝阳门。2号航站楼和朝阳门分别是出发和目的主机。而三元桥和东直门为中间的两个router。如果三元桥->>东直门段因为维修停运,我们需要更改三元桥的routing table,从而给前往朝阳门的乘客(IP包)指示:请走如下路线三元桥->>芍药居。然后依照芍药居的routing table前往朝阳门(芍药居->>东直门->>朝阳门)。一种用来生成routing table的协议是RIP(Routing Information Protocol)。它通过距离来决定routing table,所以属于distance-vector protocol。对于RIP来说,所谓的距离是从出发地到目的地途径的路由器数目(hop number)。比如上面从机场到朝阳门,按照2号航站楼->>三元桥->>东直门->>朝阳门路线,途径两个路由器,距离为2。我们最初可以手动生成三元桥的routing table。随后,根据RIP协议,三元桥向周围的路由器和主机广播自己前往各个IP的距离(比如到机场=0,团结湖=0,国贸=1,望京西=1,建国门=2)。收到RIP包的路由器和主机根据RIP包和自己到发送RIP包的主机的距离,算出自己前往各个IP的距离。东直门与三元桥的距离为1。东直门收到三元桥的RIP包(到机场的距离为0),那么东直门途径三元桥前往机场的距离为1+0=1。如果东直门自己的RIP记录都比这个远(比如东直门->>芍药居->>三元桥->>机场 = 2)。那么东直门更改自己的routing table:前往机场的交通都发往三元桥而不是芍药居。如果东直门自身的RIP记录并不差,那么东直门保持routing table不变。上述过程在各个点不断重复RIP广播/计算距离/更新routing table的过程,最终所有的主机和路由器都能生成最合理的路径(merge)。(RIP的基本逻辑是:如果A距离B为6,而我距离A为1,那么我途径A到B的距离为7)RIP出于技术上的原因(looping hops),认为距离超过15的IP不可到达。所以RIP更多用于互联网的一部分(比如整个中国电信的网络)。这样一个互联网的部分往往属于同一个ISP或者有同一个管理机构,所以叫做自治系统(AS,autonomous system)。自治系统内部的主机和路由根据通向外部的边界路由器来和其它的自治系统通信。各个边界路由器之间通过BGP(Border Gateway Protocol)来生成自己前往其它AS的routing table,而自治系统内部则参照边界路由器,使用RIP来决定routing table。BGP的基本工作过程与RIP类似,但在考虑距离的同时,也权衡比如政策、连接性能等其他因素,再决定交通的走向(routing table)。我们一开始讲述了IP包根据routing table进行接力的过程。为了顺利实现接力,我们又进一步深入到ARP和RIP/BGP。这三个协议都协助了IP传输。ARP让每台电脑和路由器知道自己局域网内IP地址和MAC地址的对应关系,从而顺利实现IP包到帧的封装。RIP协议可以生成自治系统内部合理的routing table。BGP协议可以生成自治系统外部的routing table。在整个过程中,我们都将注意力放在了IP包大的传输过程中,而故意忽略一些细节。 而上面的IP接力过程适用于IPv6。【TCP/IP详解】系列教程互联网协议入门 1互联网协议入门 2TCP-IP协议详解(1)网络协议概观TCP-IP协议详解(2) 以太网与WiFi协议TCP-IP协议详解(3) IP/ARP/RIP/BGP协议TCP-IP协议详解(4)IPv4与IPv6地址TCP-IP协议详解(5)IP协议详解TCP-IP协议详解(6) ICMP协议TCP-IP协议详解(7) UDP协议TCP-IP协议详解(8) TCP协议与流通信TCP-IP协议详解(9) TCP连接TCP-IP协议详解(10) TCP滑窗管理TCP-IP协议详解(11) TCP重传TCP-IP协议详解(12) TCP堵塞控制TCP-IP协议详解(13) DNS协议TCP-IP协议详解(14) CIDR与NATTCP-IP协议详解(15) HTTP协议概览 图解TCP-IP协议

TCP 详解
title: TCP 总结date: 2018-03-25 09:40:24tags:categories:-计算机网络我们都知道 TCP 是位于传输层的协议,他还有一个兄弟就是 UDP ,他们两共同构成了传输层。显然他们之间有很大的区别要不然的话在传输层只需要一个就好了。其中最重要的区别就是一个面向连接另外一个不是,这个区别就导致了他们是否能够保证稳定传输,显然不面向连接的 UDP 是没办法保证可靠传输的,他只能靠底层的网络层和链路层来保证。我们都知道网络层采用的是不可靠的 IP 协议。好吧,网络层也保证不了可靠传输,所以 UDP 保证可靠传输只能依靠链路层了。而 TCP 就好说了他不仅仅有底层的链路层的支持,还有自己的面向链接服务来保证可靠传输。当然 TCP也不仅仅就是比 UDP 多了一个可靠传输,前面也说到了这只是他们之间一个重要的区别。其实他的三个重要特性就是它们之间的区别。* 可靠传输* 流量控制* 拥塞控制TCP 主要是 确认重传机制 数据校验 数据合理分片和排序 流量控制 拥塞控制 依靠来完成可靠传输的 , 下面详细介绍这几种保证可靠传输的方式。确认重传,简单来说就是接收方收到报文以后给发送方一个 ACK 回复,说明自己已经收到了发送方发过来的数据。如果发送方等待了一个特定的时间还没有收到接收方的 ACK 他就认为数据包丢了,接收方没有收到就会重发这个数据包。好的,上面的机制还是比较好理解的,但是我们会发现一个问题,那就是如果接收方已经收到了数据然后返回的 ACK 丢失,发送方就会误判导致重发。而此时接收方就会收到冗余的数据,但是接收方怎么能判定这个数据是冗余的还是新的数据呢?这就涉及到了 TCP 的另外一个机制就是采用序号和确认号,也就是每次发送数据的时候这个报文段里面包括了当前报文段的序号和对上面的报文的确认号,这样我们的接收方可以根据自己接受缓存中已经有的数据来确定是否接受到了重复的报文段。这时候如果出现上面所说的 ACK 丢失,导致接受重复的报文段时客户端丢弃这个冗余的报文段。好现在我们大致了解了确认重传机制,但是还有些东西还没有弄清楚,也就是 TCP 真正的实现究竟是怎样的。这就是我们要解决的第一个问题就是如何确认。这里涉及到两种确认方式,分别称为 累计确认(捎带确认)和单停等协议。用一张图来快速理解,就是每发送一次数据,就进行一次确认。等发送方收到了 ACK 才能进行下一次的发送。一样的也是采用的 ACK 机制,但是注意一点的是,并非对于每一个报文段都进行确认,而仅仅对最后一个报文段确认,捎带的确认了上图中的 203 号及以前的报文。总结:从上面可以看到累计确认的效率更加高,首先他的确认包少一些那么也就是在网络中出现的大部分是需要传输的数据,而不是一半的数据一半的 ACK ,然后我们在第二张图中可以看到我们是可以连续发送多个报文段的(究竟一次性能发多少这个取决于发送窗口,而发送窗口又是由接受窗口和拥塞窗口一起来决定的。),一次性发多个数据会提高网络的吞吐量以及效率这个可以证明,比较简单这里不再赘述!结论:显然怎么看都是后者比较有优势,TCP 的实现者自然也是采用的累计确认的方式!上文中的那个特定的时间就是超时时间,为什么有这个值呢? 其实在发送端发送的时候就为数据启动了一个定时器,这个定时器的初始值就是超时时间。超时时间的计算其实有点麻烦,主要是我们很难确定一个确定的值,太长则进行了无意义的等待,太短就会导致冗余的包。TCP 的设计者们设计了一个计算超时时间的公式,这个公式概念比较多,有一点点麻烦,不过没关系我们一点点的来。首先我们自己思考如何设计一个超时时间的计算公式,超时时间一般肯定是和数据的传输时间有关系的,他必然要大于数据的往返时间(数据在发送端接收端往返一趟所用的时间)。好,那么我们就从往返时间下手,可是又有一个问题就是往返时间并不是固定的我们有如何确定这个值呢?自然我们会想到我们可以取一小段时间的往返时间的平均值来代表这一时间点的往返时间,也就是微积分的思想!好了我们找到了往返时间(RTT),接下来的超时时间应该就是往返时间再加上一个数就能得到超时时间了。这个数也应该是动态的,我们就选定为往返时间的波动差值,也就是相邻两个往返时间的差。下面给出我们所预估的超时时间(TimeOut)公式:很好,看到这里其实你已经差不多理解了超时时间的计算方式了,只不过我们这个公式不够完善,但是思路是对的。我们这时候来看看 TCP 的实现者们采用的方式。好的,这就是 TCP 实现的超时时间的方式,但是在实际的应用中并不是一直采用的这种方式。假如说我们现在网络状态非常的差,一直在丢包我们根本没必要这样计算,而是采用直接把原来的超时时间加倍作为新的超时时间。总结:好的现在我们知道了在两种情况下的超时时间的计算方式,正常的情况下我们采用的上面的比较复杂的计算公式,也就是RTT+波动值否则直接加倍上面我们看到在发送方等待一个超时重传时间后会开始重传,但是我们计算的超时重传时间也不定就很准,也就是说我们经常干的一件事就会是等待,而且一般等的时间还挺长。那么可不可以优化一下呢?当然,在 TCP 实现中是做了优化的,也就是这里说到的快速重传机制。他的原理就是在发送方收到三个冗余的 ACK 的时候,就开始重传那个报文段。那么为什么是三个冗余的 ACK 呢?注意三个冗余的 ACK 其实是四个 ACK 。我们先了解一下发送 ACK 策略,这个是RFC 5681 文档规定的。好的,那么现在我们可以看到如果出现了三个冗余的 ACK 他只可能是发生了两次情况三,也就是发送了两个比期望值大的数据。但是注意出现情况三有两种可能,一个是丢包,另外一个是乱序到达。比如说我们现在是数据乱序到达的,我们来看一下。第一种乱序情况另外一种乱序丢包情况结论: 很显然我们可以看到,如果发生了乱序有可能会出现三次冗余 ACK,但是如果发现了丢包必然会有三次冗余 ACK 发生,只是 ACK 数量可能更多但是不会比三次少在我们发现丢包以后我们需要重传,但是我们重传的方式也有两种方式可以选择分别是GBN和SR翻译过来就是拉回重传和选择重传。好其实我们已经能从名字上面看出来他们的作用方式了,拉回重传就是哪个地方没收到那么就从那个地方及以后的数据都重新传输,这个实现起来确实很简单,就是把发送窗口和接受窗口移回去,但是同样的我们发现这个方式不实用干了很多重复的事,效率低。那么选择重传就是你想到的谁丢了,就传谁。不存在做无用功的情况。结论:TCP 实际上使用的是两者的结合,称为选择确认,也就是允许 TCP 接收方有选择的确认失序的报文段,而不是累计确认最后一个正确接受的有序报文段。也就是跳过重传那些已经正确接受的乱序报文段。数据校验,其实这个比较简单就是头部的一个校验,然后进行数据校验的时候计算一遍 checkSum 比对一下。在 UDP 中,UDP 是直接把应用层的数据往对方的端口上 “扔” ,他基本没有任何的处理。所以说他发给网络层的数据如果大于1500字节,也就是大于MTU。这个时候发送方 IP 层就需要分片。把数据报分成若干片,使每一片都小于MTU.而接收方IP层则需要进行数据报的重组。这样就会多做许多事情,而更严重的是 ,由于UDP的特性,当某一片数据传送中丢失时 , 接收方便无法重组数据报,将导致丢弃整个UDP数据报。而在 TCP 中会按MTU合理分片,也就是在 TCP 中有一个概念叫做最大报文段长度(MSS)它规定了 TCP 的报文段的最大长度,注意这个不包括 TCP 的头,也就是他的典型值就是 1460 个字节(TCP 和 IP 的头各占用了 20 字节)。并且由于 TCP 是有序号和确认号的,接收方会缓存未按序到达的数据,根据序号重新排序报文段后再交给应用层。流量控制一般指的就是在接收方接受报文段的时候,应用层的上层程序可能在忙于做一些其他的事情,没有时间处理缓存中的数据,如果发送方在发送的时候不控制它的速度很有可能导致接受缓存溢出,导致数据丢失。相对的还有一种情况是由于两台主机之间的网络比较拥塞,如果发送方还是以一个比较快的速度发送的话就可能导致大量的丢包,这个时候也需要发送方降低发送的速度。虽然看起来上面的两种情况都是由于可能导致数据丢失而让发送主机降低发送速度,但是一定要把这两种情况分开,因为前者是属于 流量控制而后者是拥塞控制,那将是我们后面需要讨论的事情。不要把这两个概念混了。其实说到流量控制我们就不得不提一下滑动窗口协议,这个是流量控制的基础。由于 TCP 连接是一个全双工的也就是在发送的时候也是可以接受的,所以在发送端和接收端同时维持了发送窗口和接收窗口。这里为了方便讨论我们就按照单方向来讨论。接收方维持一个接受窗口,发送方一个发送窗口。发送的时候要知道接受窗口还有多少空间,也就是发送的数据量不能超过接受窗口的大小,否则就溢出了。而当我们收到一个接收方的 ACK 的时候我们就可以移动接受窗口把那些已经确认的数据滑动到窗口之外,发送窗口同理把确认的移出去。这样一直维持两个窗口大小,当接收方不能在接受数据的时候就把自己的窗口大小调整为 0 发送窗口就不会发送数据了。但是有一个问题,这个时候当接收窗口再调大的时候他不会主动通知发送方,这里采用的是发送方主动询问。还是画个图看的比较直观:拥塞控制一般都是由于网络中的主机发送的数据太多导致的拥塞,一般拥塞的都是一些负载比较高的路由,这时候为了获得更好的数据传输稳定性,我们必须采用拥塞控制,当然也为了减轻路由的负载防止崩溃。这里主要介绍两个拥塞控制的方法,一个是慢开始,另外一个称为快恢复。那么问题来了,为什么需要序号呢?为什么又是三次握手而不是两次?以及什么是 SYN 洪泛攻击?这里需要说明一下的是最后的那个长长的 TIME_WAIT 状态一般是为了客户端能够发出 ACK 一般他的值是 1分钟 或者2分钟好了,今天真的写了不少,主要就是把 TCP 的可靠传输以及连接管理讲清楚了,以及里面的一下细节问题,真的很花时间。然后其他没有涉及到的就是关于 TCP 的头并没有详细的去分析,这个东西其实也不是很难,但是现在篇幅真的已经很大就先这样,头里面的都是固定的不需要太多的理解。

TCP/IP详解卷一 ——tcp
即使端口处于2MSL状态,使用该选项,仍然能够在该端口建立连接。服务器常会设置该选项,以防服务器重启。如果在TIME_WAIT时间内,收到了对端发送来的数据报(不是重置报文段都行),那么该状态将被破坏,称为时间等待错误。原因是,当收到报文段以后,通常Seq是旧的,所以本端就会发送ACK,对端已经关闭或者是别的连接,就会发送RST,导致TIME_WAIT状态被破坏。但是许多系统规定,TIME_WAIT状态是不对重置报文段做出反应。两端同时发送FIN,两端又同时ACK。又同时进入TIME_WAIT当处于TIME_WAIT的主机崩溃以后,重启,然后需要等待相当与一个MSL的时间才能建立新的连接。这段时间成为静默时间。当一段发现到达的报文段对相关连接(也就是进程,套接字对)而言不正确的时候,TCP就会发送一个重置报文段,从而导致对端的连接快速拆卸(也就是结束吧!)。重置报文段的ACK位必须有,而且ACK的值必须在正确的窗口范围内,这样可以防止被攻击。FIN正常关闭一条连接成为有序释放,通常不会出现丢失数据的情况。重置报文段终止一条连接成为终止释放。重置报文段在任何时候都可以发送,代替FIN来终止连接,且不学校对端ACK终止报文段特性:当该数值设置为0,那么也意味着,不会再连接终止之前为了确保本端缓存中的数据都发送出去而等待。TCP在发送数据时会设置计时器,如果计时器超时认为受到数据确认信息,就会引发相应的超时,或给予计时器的重传操作,计时器超时时成为重传超时(RTO)。TCP累计确认无法返回新的ACK,或者当ACK包含选择确认信息(SACK)时,表明出现书序数据报,空洞。就会引起快速重传。若RTO短与RTT,那么没分都会重传,反之,整个网络利用率就会随之下降。RTT样本:TCP在收到数据后会返回确认信息ACK,该信息中携带一个字节的数据,测量传输该确认需要的时间,该测量结果成为RTT样本。每个连接的RTT军独立计算。如何根据RTT来设置RTO,有如下的方法公式: SRTT=a*SRTT+(1-a)RTT ,a取 0.8-0.9 。当TCP运行在RTT变化较大的网络中,无法取得期望的结果。以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补因为丢失ACK,或者实际RTT显著增长,可能出现伪超时的现象。以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补每个包可以选择各自的传送路径。某些高级路由器的采用多个并行数据链路,不同的处理演示也会导致包的离开顺序和到达顺序不匹配包的失序会造成重传,很近单嘛,前面一个小号的Seq没到达,后面的先到达,那么ACK就会 重复当TCP超时重发是,循序执行重新租宝,发送送一个更大的报文段提高性能,不超过MSS和MTU。出现在每次传送的包较小,又丢包的情况每个交互键通常会生成一个单独的数据报,也就是每个按键是独立传输的。ssh调用一个shell,对客户端输入的字符做出辉县,因此,每个字符生成4个tcp数据段,客户端的交互按键输入,服务器对按键的ACK,服务器生成的辉县,客户端对回显的ACK。通常第二段和第三段合并,成为捎带延时确认。PSH位设置,意味着发送端的缓存为空,也就是没什么可以发送了。许多情况下,TCP并不是对每个到来的包都单独的ACK,利用累计ACK可以确认之前的ACK。累计确认可以允许TCP延时一段时间发送ACK,以便将ACK和相同方向上需要传输的数据结合发。这种捎带传输的方法常用于批量数据传输。不能任意的延迟ACK,会造成重传。同时当失序发生时,必须立刻传送ACK。系统可以设置,一般延时为200-600毫秒。该算法要求,当TCP连接中有在传数据(那些已发送,但是未确认的数据)时,小的报文段就不能被发送,知道所有的数据都受到ACK。并且受到ACK后,TCP收集这些小的数据,整合到一个报文段中发送。这种方法破事TCP遵循等停规程,只有收到所有传送数据的ACK后才能继续发送新数据。该算法的不同之处在于他实现了自时钟控制,ACK返回越快,传输也越快。在相对高延迟的广域网中,更需要减小小报文的数目,该算法使得单位时间内发送的报文数目更少,RTT控制发包速率。该算法减少小包数目的同时,也增大了传输时延,也就是总的发送时间。窗口大小表明本端可用缓存大小,对端传送的数据不应该超过改大小。也表明对端发送的数据的最大大小为TCP头部ACK号和窗口大小字段之和。也就是Seq = ACK+MSSTCP活动的两端都维护一个发送窗口结构和接受窗口结构。TCP以字节为单位维护窗口结构。每个TCP报文段都包含ACK号和窗口通告信息,TCP发送端可以据此调节窗口结构。窗口左边界不能左移。窗口的动作分为,关闭(收到ACK,左边右移),打开(MSS扩大,右边右移),收缩(MSS减小,右边左移)当收到ACK号增大,而MSS不变时窗口向前滑动当当左边界与右边界相等时,成为零窗口,此时发送端不能在发送新的数据,这种情况下,TCP开始探测对端窗口,伺机增大窗口。当接受窗口值变为0是,可以邮箱的组织发送端继续发送,知道窗口大小回复为非0值。当接收端窗口得到可用空间是,就会给发送端传输一个窗口更新,告知器可以继续发送数据,这样的这样的窗口更新通常不包含数据,成为纯ACK,因此不能保证传输的可靠性。如果一端的窗口更新ACK丢失,通信双方就会处于等待状态。为避免这种情况发生。发送端会采用一个持续计时器间歇性的查询接收端,看其窗口是否已经增长。持续计时器会触发窗口探测的传输,强制要求对端返回ACK。窗口探测包包含一个字节数据,采用TCP传输,因此可以避免窗口更新丢失导致的死锁。因为包含一个字节数据Seq改变,接受端必须处理,如果接受就会ACK。窗口大小还是0,那么就会丢弃该报,没有响应。这时候发送端会持续的发送窗口探测包。当接收端通告窗口较小,或者发送端发送的数据较小。这样数据报的有效携带率小,耗费网络资源多。避免方法以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补以后再补

tcp连接状态详解
unix的哲学是一切皆文件,可以把socket看成是一种特殊的文件,而一些socket函数就是对其进行的操作api(读/写IO、打开、关闭)。我们知道普通文件的打开操作(open)返回一个文件描述字,与之类似,socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,sockfd即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。在将一个地址绑定到socket的时候,需要先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于这个问题曾引发过不少血案,谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再赋给socket。这里的主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:listen函数的第一个参数即为要监听的socket描述字,第二个参数为socket可以接受的排队的最大连接个数。listen函数表示等待客户的连接请求。connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数去接收请求,这样连接就建立好了(在connect之后就建立好了三次连接),之后就可以开始进行类似于普通文件的网络I/O操作了。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与客户的TCP连接。accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数。失败时返回-1,并设置errno变量。 在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是全部的数据。2)返回的值小于0,此时出现了错误在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,类似于操作完打开的文件要调用fclose关闭打开的文件。close一个TCP socket的缺省行为时把该socket标记为已关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:客户端向服务器发送一个SYN J服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1客户端再想服务器发一个确认ACK K+1socket中TCP的四次握手释放连接详解某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。一段时间之后,服务端调用close关闭它的socket。这导致它的TCP也发送一个FIN N;接收到这个FIN的源发送端TCP对它进行确认,这样每个方向上都有一个FIN和ACK。为什么要三次握手由于tcp连接是全双工的,存在着双向的读写通道,每个方向都必须单独进行关闭。当一方完成它的数据发送任务后就可以发送一个FIN来终止这个方向的连接。收到FIN只意味着这个方向上没有数据流动,但并不表示在另一个方向上没有读写,所以要双向的读写关闭需要四次握手,3. time_wait状态如何避免?首先服务器可以设置SO_REUSEADDR套接字选项来通知内核,如果端口忙,但TCP连接位于TIME_WAIT状态时可以重用端口。在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME_WAIT状态。1.客户端连接服务器的80服务,这时客户端会启用一个本地的端口访问服务器的80,访问完成后关闭此连接,立刻再次访问服务器的80,这时客户端会启用另一个本地的端口,而不是刚才使用的那个本地端口。原因就是刚才的那个连接还处于TIME_WAIT状态。2.客户端连接服务器的80服务,这时服务器关闭80端口,立即再次重启80端口的服务,这时可能不会成功启动,原因也是服务器的连接还处于TIME_WAIT状态。实战分析:状态描述:CLOSED:无连接是活动的或正在进行LISTEN:服务器在等待进入呼叫SYN_RECV:一个连接请求已经到达,等待确认SYN_SENT:应用已经开始,打开一个连接ESTABLISHED:正常数据传输状态FIN_WAIT1:应用说它已经完成FIN_WAIT2:另一边已同意释放ITMED_WAIT:等待所有分组死掉CLOSING:两边同时尝试关闭TIME_WAIT:另一边已初始化一个释放LAST_ACK:等待所有分组死掉命令解释:如何尽量处理TIMEWAIT过多?编辑内核文件/etc/sysctl.conf,加入以下内容:net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间然后执行 /sbin/sysctl -p 让参数生效./etc/sysctl.conf是一个允许改变正在运行中的Linux系统的接口,它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,修改内核参数永久生效。简单来说,就是打开系统的TIMEWAIT重用和快速回收。本文主要讲述了socket的主要api,以及tcp的连接过程和其中各个阶段的连接状态,理解这些是更深入了解tcp的基础!

本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/302456.html。