文章目录
- 简介
- 什么是TCP/IP
- OSI模型的七层框架
- TCP/IP 协议族
- 应用层
- 传输层
- TCP和UDP
- 区别
- 应用
- 网络层
- 链路层
- 物理层
- 路由器的由来
- 链路层: 交换机
- 传输层:IP地址和路由器
- IP地址由来
- 路由器的诞生
- 子网的由来
- 参考的网络拓扑图
- HTTP报文传输过程
- 数据封装和分用
- TCP各个状态
- 全部11种状态
- TCP状态迁移
- 建立连接
- 关闭连接
- 问题
- 为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间?
- 如果已经建立了连接,但是Client端突然出现故障了怎么办?
- TCP拥塞控制
- 慢开始和拥塞避免
- 慢开始算法
- 拥塞避免
- 快重传和快恢复
- 快重传
- 快恢复
- 服务器端主动关闭网络:
- TCP--wireshark【解析方法】
- 网络不稳定时,提示ppp open -2
- 当缓存数据到达900K的时候,会存在丢包现象
- M35F模块连接网络后,拨打电话,会出现掉线,数据不能正常传输的情况
- 超时重传
简介
TCP/IP内容整理
什么是TCP/IP
TCP/IP不是一个协议,而是一个协议族的统称(TCP/IPProtocols),简称TCP/IP。
TCP/IP协议族提供了点对点的连结机制,并且将传输数据帧的封装、寻址、传输、路由以及接收方式,都予以标准化。
OSI模型的七层框架
OSI参考模型,即开放式系统互联模型(Open System Interconnect),是ISO组织在1985年发布的网络互连模型。
OSI模型定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),每一层实现各自的功能和协议,并完成与相邻层的接口通信。
框架 | 内容 |
---|---|
应用层 | HTTP、FTP、Telnet 等等 |
表示层 | XDR、ASN.1、SMB、AFP、NCP、等等 |
会话层 | ASAP、SSH、RPC、NetBIOS、等等 |
传输层 | TCP、UDP、RTP、SCTP 等等 |
网络层 | IP、ICMP,IGMP等等 |
数据链路层 | 以太网,PPP, ATM等等 |
物理层 | 铜缆、网线、光缆、无线电等等 |
TCP/IP 协议族
TCP/IP协议中,OSI七层被简化为了四个层次。
TCP/IP模型中的各种协议,依其功能不同,被分别归属到这四层之中,常被视为是简化过后的七层OSI模型。
应用层
应用层的主要协议有HTTP(万维网服务)、SMTP(电子邮件)、SSH(安全远程登陆)、DNS(域名解析)、FTP(文件传输)等,是用来读取来自传输层的数据或者将数据传输写入传输层;
传输层
传输层的主要协议有UDP、TCP,传输层的协议,解决了诸如端到端可靠性问题,能确保数据可靠的到达目的地。
传输层的主要功能大致如下:
- 为端到端连接提供传输服务;
- 这种传输服务分为可靠和不可靠的,其中TCP是典型的可靠传输,而UDP则是不可靠传输;
- 为端到端连接提供流量控制、差错控制、QoS(Quality of Service)服务质量等管理服务。
TCP和UDP
TCP协议是一个面向连接的、可靠的传输协议,它提供一种可靠的字节流,能保证数据完整、无损并且按顺序到达。TCP尽量连续不断地测试网络的负载并且控制发送数据的速度以避免网络过载。另外,TCP试图将数据按照规定的顺序发送。
UDP协议是一个无连接的数据报协议,是一个“尽力传递”和“不可靠”协议,不会对数据包是否已经到达目的地进行检查,并且不保证数据包按顺序到达。
区别
面向报文
面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。
面向字节流
面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。
应用
什么时候应该使用TCP?
当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。
什么时候应该使用UDP?
当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。
DNS
DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。DNS协议运行在UDP协议之上,使用端口号53。
网络层
网络层的主要协议有ICMP、IP、IGMP,网络层负责将数据传输到目标地址,目标地址可以是多个网络通过路由器连接而成的某一个地址。网络层负责寻找合适的路径到达对方计算机,并把数据帧传送给对方,网络层还可以实现拥塞控制、网际互连等功能。
链路层
链路层的主要协议有ARP、RARP, 数据的传输单位为比特。用来处理连接网络的硬件部分。该层既包括操作系统硬件的设备驱动、NIC(网卡)、光纤等物理可见部分,还包括连接器等一切传输媒介。
物理层
物理层:使用MAC解决设备的身份证问题。
MAC地址是全局唯一的标识,每一台设备都有一个唯一的MAC地址。
数据链路层负责将0、1序列划分为数据帧从一个节点传输到临近的另一个节点, 这些节点是通过MAC来唯一标识的。
路由器的由来
链路层: 交换机
交换机内部维护一张 MAC 地址表,记录着每一个 MAC 地址的设备,连接在其哪一个端口上.
MAC 地址表的映射
当然最开始的时候,MAC地址表是空的,是怎么逐步建立起来的呢?
假如在 MAC 地址表为空是,你给 B 发送了如下数据
由于这个包从端口 4 进入的交换机,所以此时交换机就可以在 MAC地址表记录第一条数据:
MAC:aa-aa-aa-aa-aa-aa-aa 端口:4
交换机看目标 MAC 地址(bb-bb-bb-bb-bb-bb)在地址表中并没有映射关系,于是将此包发给了所有端口,也即发给了所有机器。
之后,只有机器 B 收到了确实是发给自己的包,于是做出了响应,响应数据从端口 1 进入交换机,于是交换机此时在地址表中更新了第二条数据:
MAC:bb-bb-bb-bb-bb-bb 端口:1
经过该网络中的机器不断地通信,交换机最终将 MAC 地址表建立完毕。
随着机器数量越多,交换机的端口也不够了,但聪明的你发现,只要将多个交换机连接起来,这个问题就轻而易举搞定
这在只有 8 台电脑的时候还好,甚至在只有几百台电脑的时候,都还好,所以这种交换机的设计方式,已经足足支撑一阵子了。
但很遗憾,人是贪婪的动物,很快,电脑的数量就发展到几千、几万、几十万。
传输层:IP地址和路由器
交换机已经无法记录如此庞大的映射关系了。
此时你动了歪脑筋,你发现了问题的根本在于,连出去的那根红色的网线,后面不知道有多少个设备不断地连接进来,从而使得地址表越来越大。
那我可不可以让那根红色的网线,接入一个新的设备,这个设备就跟电脑一样有自己独立的 MAC 地址,而且同时还能帮我把数据包做一次转发呢?
这个设备就是路由器,它的功能就是,作为一台独立的拥有 MAC 地址的设备,并且可以帮我把数据包做一次转发,你把它定在了网络层。
注意,路由器的每一个端口,都有独立的 MAC 地址
IP地址由来
现在每一台电脑,同时有自己的 MAC 地址,又有自己的 IP 地址,只不过 IP 地址是软件层面上的,可以随时修改,MAC 地址一般是无法修改的。
如上图所示,假如我想要发送数据包给 ABCD 其中一台设备,不论哪一台,我都可以这样描述,“将 IP 地址为 192.168.0 开头的全部发送给到路由器,之后再怎么转发,交给它!”,巧妙吧。
路由器的诞生
路由器诞生了,专门负责IP地址的寻找。那报文交给路由器之后,路由器又是怎么把数据包准确转发给指定设备的呢?
现在两个设备之间传输,除了加上数据链路层的头部之外,还要再增加一个网络层的头部。
假如 A 给 B 发送数据,由于它们直接连着交换机,所以 A 直接发出如下数据包即可,其实网络层没有体现出作用。
但假如 A 给 C 发送数据,A 就需要先转交给路由器,然后再由路由器转交给 C。由于最底层的传输仍然需要依赖以太网,所以数据包是分成两段的
A ~ 路由器这段的包如下:
路由器到 C 这段的包如下:
好了,上面说的两种情况(A->B,A->C),相信细心的读者应该会有不少疑问,下面我们一个个来展开。
子网的由来
A 给 C 发数据包,怎么知道是否要通过路由器转发呢?
答案:子网
如果源 IP 与目的 IP 处于一个子网,直接将包通过交换机发出去。
如果源 IP 与目的 IP 不处于一个子网,就交给路由器去处理。
好,那现在只需要解决,什么叫处于一个子网就好了。
192.168.0.1 和 192.168.0.2 处于同一个子网
192.168.0.1 和 192.168.1.1 处于不同子网
这两个是我们人为规定的,即我们想表示,对于 192.168.0.1 来说:
192.168.0.xxx 开头的,就算是在一个子网,否则就是在不同的子网。
那对于计算机来说,怎么表达这个意思呢?于是人们发明了子网掩码的概念
假如某台机器的子网掩码定为 255.255.255.0
这表示,将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网,就这么简单。
比如
A电脑:192.168.0.1 & 255.255.255.0 = 192.168.0.0
B电脑:192.168.0.2 & 255.255.255.0 = 192.168.0.0
C电脑:192.168.1.1 & 255.255.255.0 = 192.168.1.0
D电脑:192.168.1.2 & 255.255.255.0 = 192.168.1.0
那么 A 与 B 在同一个子网,C 与 D 在同一个子网,但是 A 与 C 就不在同一个子网,与 D 也不在同一个子网,以此类推。
所以如果 A 给 C 发消息,A 和 C 的 IP 地址分别 & A 机器配置的子网掩码,发现不相等,则 A 认为 C 和自己不在同一个子网,于是把包发给路由器,就不管了,之后怎么转发,A 不关心。
A 如何知道,哪个设备是路由器?
答案:在 A 上要设置默认网关
上一步 A 通过是否与 C 在同一个子网内,判断出自己应该把包发给路由器,那路由器的 IP 是多少呢?
其实说发给路由器不准确,应该说 A 会把包发给默认网关。
对 A 来说,A 只能直接把包发给同处于一个子网下的某个 IP 上,所以发给路由器还是发给某个电脑,对 A 来说也不关心,只要这个设备有个 IP 地址就行。
所以默认网关,就是 A 在自己电脑里配置的一个 IP 地址,以便在发给不同子网的机器时,发给这个 IP 地址。
路由表的由来(和Mac表的由来好像,都是逼出来的)
路由器如何知道C在哪里?
答案:路由表
现在 A 要给 C 发数据包,已经可以成功发到路由器这里了,最后一个问题就是,路由器怎么知道,收到的这个数据包,该从自己的哪个端口出去,才能直接(或间接)地最终到达目的地 C 呢。
路由器收到的数据包有目的 IP 也就是 C 的 IP 地址,需要转化成从自己的哪个端口出去,很容易想到,应该有个表,就像 MAC 地址表一样。
这个表就叫路由表。
至于这个路由表是怎么出来的,有很多路由算法,本文不展开,因为我也不会哈哈~
不同于 MAC 地址表的是,路由表并不是一对一这种明确关系,我们下面看一个路由表的结构。
目的地址 | 子网掩码 | 下一跳 | 端口 |
---|---|---|---|
192.168.0.0 | 255.255.255.0 | 0 | |
192.168.0.254 | 255.255.255.255 | 0 | |
192.168.1.0 | 255.255.255.0 | 1 | |
192.168.1.254 | 255.255.255.255 | 1 | |
这就很好理解了,路由表就表示,192.168.0.xxx 这个子网下的,都转发到 0 号端口,192.168.1.xxx 这个子网下的,都转发到 1 号端口。下一跳列还没有值,我们先不管 |
刚才说的都是 IP 层,但发送数据包的数据链路层需要知道 MAC 地址,可是我只知道 IP 地址该怎么办呢?
答案:arp
假如你(A)此时不知道你同伴 B 的 MAC 地址(现实中就是不知道的,刚刚我们只是假设已知),你只知道它的 IP 地址,你该怎么把数据包准确传给 B 呢?
答案很简单,在网络层,我需要把 IP 地址对应的 MAC 地址找到,也就是通过某种方式,找到 192.168.0.2 对应的 MAC 地址 BBBB。
这种方式就是 arp 协议,同时电脑 A 和 B 里面也会有一张 arp 缓存表,表中记录着 IP 与 MAC 地址的对应关系。
IP 地址 | MAC 地址 |
---|---|
192.168.0.2 | BBBB |
一开始的时候这个表是空的,电脑 A 为了知道电脑 B(192.168.0.2)的 MAC 地址,将会广播一条 arp 请求,B 收到请求后,带上自己的 MAC 地址给 A 一个响应。此时 A 便更新了自己的 arp 表。 | |
这样通过大家不断广播 arp 请求,最终所有电脑里面都将 arp 缓存表更新完整。 |
图解:整个传输过程
从各个节点的视角来看
电脑视角:
首先我要知道我的 IP 以及对方的 IP
通过子网掩码判断我们是否在同一个子网
在同一个子网就通过 arp 获取对方 mac 地址直接扔出去
不在同一个子网就通过 arp 获取默认网关的 mac 地址直接扔出去
交换机视角:
我收到的数据包必须有目标 MAC 地址
通过 MAC 地址表查映射关系
查到了就按照映射关系从我的指定端口发出去
查不到就所有端口都发出去
路由器视角:
我收到的数据包必须有目标 IP 地址
通过路由表查映射关系
查到了就按照映射关系从我的指定端口发出去(不在任何一个子网范围,走其路由器的默认网关也是查到了)
查不到则返回一个路由不可达的数据包
如果你嗅觉足够敏锐,你应该可以感受到下面这句话:
网络层(IP协议)本身没有传输包的功能,包的实际传输是委托给数据链路层(以太网中的交换机)来实现的。
涉及到的三张表分别是
交换机中有 MAC 地址表用于映射 MAC 地址和它的端口
路由器中有路由表用于映射 IP 地址(段)和它的端口
电脑和路由器中都有arp 缓存表用于缓存 IP 和 MAC 地址的映射关系
这三张表是怎么来的
MAC 地址表是通过以太网内各节点之间不断通过交换机通信,不断完善起来的。
路由表是各种路由算法 + 人工配置逐步完善起来的。
arp 缓存表是不断通过 arp 协议的请求逐步完善起来的。
知道了以上这些,目前网络上两个节点是如何发送数据包的这个过程,就完全可以解释通了!
参考的网络拓扑图
那接下来我们就放上参考的 最后一个网络拓扑图吧,请做好 战斗 准备!
这时路由器 1 连接了路由器 2,所以其路由表有了下一跳地址这一个概念,所以它的路由表就变成了这个样子。如果匹配到了有下一跳地址的一项,则需要再次匹配,找到其端口,并找到下一跳 IP 的 MAC 地址。
也就是说找来找去,最终必须能映射到一个端口号,然后从这个端口号把数据包发出去。
目的地址 | 下一跳 | 端口 |
---|---|---|
192.168.0.0/24 | 0 | |
192.168.0.254/32 | 0 | |
192.168.1.0/24 | 1 | |
192.168.1.254/32 | 1 | |
192.168.2.0/24 | 192.168.100.5 | |
192.168.100.0/24 | 2 | |
192.168.100.4/32 | 2 | |
这时如果 A 给 F 发送一个数据包,能不能通呢?如果通的话整个过程是怎样的呢? |
详细过程文字描述:
- 首先 A(192.168.0.1)通过子网掩码(255.255.255.0)计算出自己与 F(192.168.2.2)并不在同一个子网内,于是决定发送给默认网关(192.168.0.254)
- A 通过 ARP 找到 默认网关 192.168.0.254 的 MAC 地址。
- A 将源 MAC 地址(AAAA)与网关 MAC 地址(ABAB)封装在数据链路层头部,又将源 IP 地址(192.168.0.1)和目的 IP 地址(192.168.2.2)(注意这里千万不要以为填写的是默认网关的 IP 地址,从始至终这个数据包的两个 IP 地址都是不变的,只有 MAC 地址在不断变化)封装在网络层头部,然后发包
- 交换机 1 收到数据包后,发现目标 MAC 地址是 ABAB,转发给路由器1
- 数据包来到了路由器 1,发现其目标 IP 地址是 192.168.2.2,查看其路由表,发现了下一跳的地址是 192.168.100.5
- 所以此时路由器 1 需要做两件事,第一件是再次匹配路由表,发现匹配到了端口为 2,于是将其封装到数据链路层,最后把包从 2 号口发出去。
- 此时路由器 2 收到了数据包,看到其目的地址是 192.168.2.2,查询其路由表,匹配到端口号为 1,准备从 1 号口把数据包送出去。
- 但此时路由器 2 需要知道 192.168.2.2 的 MAC 地址了,于是查看其 arp 缓存,找到其 MAC 地址为 FFFF,将其封装在数据链路层头部,并从 1 号端口把包发出去。
- 交换机 3 收到了数据包,发现目的 MAC 地址为 FFFF,查询其 MAC 地址表,发现应该从其 6 号端口出去,于是从 6 号端口把数据包发出去。
- F 最终收到了数据包!并且发现目的 MAC 地址就是自己,于是收下了这个包.
HTTP报文传输过程
以一个HTTP请求的传输为例,请求从HTTP客户端(如浏览器)和HTTP服务端应用的传输过程,大致如下图所示:
数据封装和分用
加上特定标识的过程叫做数据的封装,在数据使用的时候再去掉特定标识,去掉特定标识的过程就叫做分用。
TCP/IP协议的数据封装和分用过程,大致如下图所示:
在数据封装时,数据经过每个层都会打上该层特定标识,添加上头部。
在传输层封装时,添加的报文首部时要存入一个应用程序的标识符,无论TCP和UDP都用一个16位的端口号来表示不同的应用程序,并且都会将源端口和目的端口存入报文首部中。
在网络层封装时,IP首部会标识处理数据的协议类型,或者说标识出网络层数据帧所携带的上层数据类型,如TCP、UDP、ICMP、IP、IGMP等等。
具体来说,会在IP首部中存入一个长度为8位的数值,称作协议域:
1表示为ICMP协议、2表示为IGMP协议、6表示为TCP协议、17表示为UDP协议、等等。
IP首部还会标识发送方地址(源IP)和接收方地址(目标IP)。
在链路层封装时,网络接口分别要发送和接收IP、ARP和RARP等多种不同协议的报文,因此也必须在以太网的帧首部中加入某种形式的标识,以指明所处理的协议类型,为此,以太网的报文帧的首部也有一个16位的类型域,标识出以太网数据帧所携带的上层数据类型,如IPv4、ARP、IPV6、PPPoE等等。
数据封装和分用的过程大致为:发送端每通过一层会增加该层的首部,接收端每通过一层则删除该层的首部。
总体来说,TCP/IP分层管理、数据封装和分用的好处:分层之后若需改变相关设计,只需替换变动的层。各层之间的接口部分规划好之后,每个层次内部的设计就可以自由改动。层次化之后,设计也变得相对简单:各个层只需考虑分派给自己的传输任务。
在传输过程中,数据报文会在不同的物理网络之间传递,还是以一个HTTP请求的传输为例,请求在不同物理网络之间的传输过程,大致如下图所示:
数据包在不同物理网络之间的传输过程中,网络层会通过路由器去对不同的网络之间的数据包进行存储、分组转发处理。构造互连网最简单的方法是把两个或多个网络通过路由器进行连接。路由器可以简单理解为一种特殊的用于网络互连的硬件盒,其作用是为不同类型的物理网络提供连接:以太网、令牌环网、点对点的链接和FDDI(光纤分布式数据接口)等等。
物理网络之间通过路由器进行互连,随着增加不同类型的物理网络,可能会有很多个路由器,但是对于应用层来说仍然是一样的,TCP协议栈为大家屏蔽了物理层的复杂性。总之,物理细节和差异性的隐藏,使得互联网TCP/IP传输的功能变得非常强大.
TCP各个状态
全部11种状态
- 客户端独有的:
- SYN_SENT
- FIN_WAIT1
- FIN_WAIT2
- CLOSING
- TIME_WAIT
- 服务器独有的:
- LISTEN
- SYN_RCVD
- CLOSE_WAIT
- LAST_ACK
- 共有的:
- CLOSED
- ESTABLISHED
标识 | 说明 |
---|---|
SYN-SENT | 在发送连接请求后等待匹配的连接请求 |
SYN-RECEIVED | 在收到和发送一个连接请求后等待对连接请求的确认 |
ESTABLISHED | 代表一个打开的连接,数据可以传送给用户 |
FIN-WAIT-1 | 等待远程TCP的连接中断请求,或先前的连接中断请求的确认 |
FIN-WAIT-2 | 从远程TCP等待连接中断请求 |
CLOSE-WAIT | 等待从本地用户发来的连接中断请求 |
CLOSING | 等待远程TCP对连接中断的确认 |
LAST-ACK | 等待原来发向远程TCP的连接中断请求的确认 |
TIME-WAIT | 等待足够的时间以确保远程TCP接收到连接中断请求的确认 |
CLOSED | 没有任何连接状态 |
TCP状态迁移
建立连接
采用三次握手建立一个连接。
- 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。完成三次握手,客户端与服务器开始传送数据.
关闭连接
采用四次挥手关闭连接。
-
第一次挥手:主动断开方(客户端,服务的都可以)向对方发送一个FIN结束请求报文,并设置序列号和确认号,随后主动断开方进入FIN_WAIT1状态,这表示主动断开方已经没有业务数据要发给对方了,准备关闭SOCKET连接了。
-
第二次挥手:被动断开方收到FIN断开请求后会发送一个ACK响应报文,表明同意断开请求。随后被动断开方就进入CLOSE-WAIT状态(等待关闭状态),此时若被动断开方还有数据要发送给主动方,主动方还会接受。被动方会持续一段时间。
主动方收到ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。 -
第三次挥手:被动断开方的CLOSE-WAIT(等待关闭)结束后,被动方会向主动方发送一个FIN+ACK报文
表示被动方的数据都发完了。然后被动方进入LAST_ACK状态。 -
第四次挥手:主动断开方收到FIN+ACK断开响应报文后,还需进行最后确认,向被动方发送一个ACK确认报文,然后主动方进入TIME_WAIT状态,在等待完成2MSL时间后,如果期间没有收到被动方的报文,则证明对方已正常关闭,主动断开方的连接最终关闭。
被动方在收到主动方第四次挥手发来的ACK报文后,就关闭了连接。 -
客户端TCP状态迁移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED -
服务器TCP状态迁移:
CLOSED->LISTEN->SYN_RECV ->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
问题
为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间?
主动断开方等待2MSL的时间,是为了确保两端都能最终关闭。假设网络是不可靠的,被动断开方发送FIN+ACK报文后,其主动方的ACK响应报文有可能丢失,这时候的被动断开方处于LAST-ACK状态的,由于收不到ACK确认被动方一直不能正常的进入CLOSED状态。在这种场景下,被动断开方会超时重传FIN+ACK断开响应报文,如果主动断开方在2MSL时间内,收到这个重传的FIN+ACK报文,会重传一次ACK报文,后再一次重新启动2MSL计时等待,这样,就能确保被动断开方能收到ACK报文,从而能确保被动方顺利进入到CLOSED状态。只有这样,双方都能够确保关闭。反过来说,如果主动断开方在发送完ACK响应报文后,不是进入TIME_WAIT状态去等待2MSL时间,而是立即释放连接,则将无法收到被动方重传的FIN+ACK报文,所以不会再发送一次ACK确认报文,此时处于LAST-ACK状态的被动断开方,无法正常进入到CLOSED状态。防止“旧连接的已失效的数据报文”出现在新连接中。主动断开方在发送完最后一个ACK报文后,再经过2MSL,才能最终关闭和释放端口,这就意味着,相同端口的新TCP新连接,需要在2MSL的时间之后,才能够正常的建立。2MSL这段时间内,旧连接所产生的所有数据报文,都已经从网络中消失了,从而,确保了下一个新的连接中不会出现这种旧连接请求报文。
如果已经建立了连接,但是Client端突然出现故障了怎么办?
TCP还设有一个保活计时器,Client端如果出现故障,Server端不能一直等下去,这样会浪费系统资源。每收到一次Client客户端的数据帧后,Server端都的保活计时器会复位。计时器的超时时间通常是设置为2小时,若2小时还没有收到Client端的任何数据帧,Server端就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,Server端就认为Client端出了故障,接着就关闭连接。如果觉得保活计时器的两个多小时的间隔太长,可以自行调整TCP连接的保活参数。
TCP拥塞控制
慢开始和拥塞避免
发送方维持一个拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。
慢开始算法
当主机开始发送数据时,如果立即将所有数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是 先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚刚开始发送报文段时,先把拥塞窗口cwnd设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口cwnd,可以使分组注入到网络的速率更加合理。
每经过一个传输轮次,拥塞窗口cwnd就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。不过“传输轮次”更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。另,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。慢开始门限ssthresh的用法如下:
当 cwnd < ssthresh 时,使用上述的慢开始算法。
当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。
拥塞避免
让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限ssthresh设置为出现拥塞时的发送 方窗口值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生 拥塞的路由器有足够时间把队列中积压的分组处理完毕。
快重传和快恢复
快重传
快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时才进行捎带确认。
接收方收到了M1和M2后都分别发出了确认。现在假定接收方没有收到M3但接着收到了M4。显然,接收方不能确认M4,因为M4是收到的失序报文段。根据 可靠传输原理,接收方可以什么都不做,也可以在适当时机发送一次对M2的确认。但按照快重传算法的规定,接收方应及时发送对M2的重复确认,这样做可以让 发送方及早知道报文段M3没有到达接收方。发送方接着发送了M5和M6。接收方收到这两个报文后,也还要再次发出对M2的重复确认。这样,发送方共收到了 接收方的四个对M2的确认,其中后三个都是重复确认。
快重传算法还规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段M3,而不必 继续等待M3设置的重传计时器到期。
由于发送方尽早重传未被确认的报文段,因此采用快重传后可以使整个网络吞吐量提高约20%。
快恢复
与快重传配合使用的还有快恢复算法,其过程有以下两个要点:
当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半。
与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口cwnd现在不设置为1),而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
服务器端主动关闭网络:
TCP–wireshark【解析方法】
-
接收窗口。
win=xxx 告诉对方,我的接收窗口大小。 -
当我的接收窗口满了,也就是win=0,Wireshark显示【TCP ZeroWindow】,这个时候,对方不能再发送数据。
-
发送窗口数据分为三类:
发送了已经被确认
发送了还没有被确认
待发送的数据
当待发送的数据为0,也就是发了出去,但是都没有确认,允许发送的大小为0,也就是说,在途字节数等于对方的接收窗口,这个时候,Wireshark打上【TCP window Full】标记,表示我不能再发送数据了 -
在途字节数,是站在发送者的角度,表示的概念是,我已经发了多少,减去对方最近的一次确认,确认了多少。也就是 Seq + Len - Ack【最近的一次Ack】
-
正常情况下,后一个包的Seq = 前一个包的Seq+Len
-
正常情况下,对方的Ack = 我刚发的Seq+Len
-
但是,三次握手和四次挥手是例外,syn和fin标志,Len是0,但是序号都加1,因为为了保证可靠性。ack标志,序号不加1,否则变成了死循环,我的ack你确认,你的ack我再确认,无法完成挥
网络不稳定时,提示ppp open -2
PC端网络服务中心,进行频繁的上下线操作 (每隔10秒左右操作开关一次),同时串口以1K/500ms的速度发送报文
测试使用的是AD2000 模块使用EC20
原因:导致模块进入数据态的时候,DTU还认为是处于数据态,进行不能正常进行at命令处理
修改方案:当pppOpen 返回err的时候, 强制使用pppClose(0),来关闭连续,并且重新再一次尝试pppOpen,来确保链路完全断开了。
说明:由于模块发送结束报文和DTU发送结束报文的先后顺序不同 引起的异常
-
正常流程:DTU先发送掉线结束报文
DTU 发送User request 报文 FSM状态:LS_OPENED
FSM的状态LS_OPENED切换到LS_CLOSING
DTU收到模块发送的结束报文
断开连接(LS_CLOSING -》LS_CLOSED)pppControl.openFlag=0
此时退出函数,重新拨号 -
异常流程: 模块先发送掉线结束报文
DTU收到模块发送的结束报文 FSM状态:LS_OPENED
断开连接(必须要FSM状态是LS_CLOSING才能关闭)
异常1 : pppControl的openFlag标志位 没有清除
异常2 : (LS_CLOSING -》LS_CLOSING ) 状态没有改变
DTU 发送User request 报文
FSM的状态LS_OPENED切换到LS_CLOSING
因为DTU没有在收到模块发送的结束报文
导致了FSM的状态一直在LS_CLOSING
此时退出线程,重新初始化 FSM的状态 LS_INITIAL
重新拨号,发送AT: ATDT*99#,回复成功(此时模块进入数据态)
打开PPP拨号,pppControl.openFlag=1
因此认定此时PPP拨号在线,所以提示错误 ppp open -2
当缓存数据到达900K的时候,会存在丢包现象
原因: 当tcp_sndbuf 小于要发送的长度时候,此时,没有对tcp_sndbuf进行判断,导致了将串口的数据继续写入,此时部分数据不能发送到网口。
修改点: 要先判断tcp_sndbuf 大于发送长度的时候再发送,否则返回发送失败,等到下一次发送缓存足够了在发送。否则网络发送缓存还没准备好就把数据发送过去,返回结果不是-1,错误的 认为发送成功,会导致丢包
M35F模块连接网络后,拨打电话,会出现掉线,数据不能正常传输的情况
原因:因为M35F属于2G模块,电话和数据占用同一个信道,当拨打电话时候,优先处理电话逻辑,导致了数据断开,此时需要重新拨号才能正常联网。
修改点:设置M35F模块,配置为不可拨打电话状态,如果客户需要用到电话激活或者短信激活功能时候,需要使用别的模块
超时重传
- DTU的重发间隔的时间:rto是由RTT(数据来回一次的时间)计算获得,然后再karn算法进行计算获得对应的RTO时间,重发的时
间是动态的,由算法获得。 - DTU的重发次数:遵循二进制指数退避(前一次的两倍时间),lwip里面定义,当重传超过6次的时候, RTO的值不再退避。