前言
上一篇文章具体总结了UDP和TCP内部的一些机制,这篇文章续上一篇的内容继续总结。
TCP的10个核心机制:
1.确认应答(可靠性) | 返回ack(应答报文) |
2.超时重传(可靠性) | 超过一定时间不返回ack,重新发送数据 |
3.连接管理(保证可靠性的基础) | 三次握手,四次挥手 |
4.滑动窗口(批量传输数据) | 等待ack花大量时间,批量传输数据,减少等待时间 |
5.流量控制 | 针对于发送方和接收方保证数据可靠性 |
6.拥塞机制 | 针对网络传输路径的数据保证可靠性 |
7.延时应答 | ack晚会发,让缓冲区的数据被消费一批 |
8.捎带应答 | 两个可能合并成一个的数据包合并成一个,提高效率 |
9.面向字节流(粘包问题) | 代码层次定义分割符来标志数据缓冲区的每一个数据包 |
10.异常情况 | TCP对网络异常的处理 |
上一篇文章已经介绍完流量控制,所以看接下来的内容:
6.拥塞机制
滑动窗口的大小取决于:流量控制和拥塞机制。(流量控制衡量了接收方的处理能力,拥塞控制衡量了传输路径的处理能力),如下图:
很明显的问题就是:主机A和主机B有了流量控制机制,保证了发送数据时不会超过处理能力的瓶颈,但是中间的节点要转发很多的数据包,此时他们的接受能力也是有一定的上限的,此时如果超过了上限,也会出现丢包问题。
TCP针对这个问题就有了拥塞控制机制:衡量中间节点的传输能力,看每个节点当前的情况是啥样的,并且规划路线,看走哪一条路径比较合适。(通过实验的方式来衡量)如下图:
- 开始传输数据的时候先发的慢一点(由于这个数据较小,远达不到路径传输的瓶颈)
- 如果不出现丢包的情况,就增大传输数据的速率(扩大窗口的大小)
- 如果开始出现丢包了,就减小传输数据的速率。
从长远的时间角度来看上图内容,传输路径中的节点是一致保持在动态平衡中的。
7.延时应答
数据发给接收方了,此时需要返回一个ack,要返回ack,但不是立即发,而是等一小会再发。
由于流量控制机制我们了解到,窗口大小(批量发送数据的多少)就决定了TCP在传输数据的效率,其中流量控制是指接收方的缓冲区中剩余空间的大小,那是如何提高效率的呢?
如下图:
上图中,如果发送完数据立即返回一个ack,此时可能缓冲区的数据还没被读取呢,剩余的空间也就比较小,返回ack中的下一次发送数据的窗口也就比较小;如果是等应用程序读取一批数据后,此时缓冲区剩余的空间也就大了,下一次发送方发数据也就可以多发一批(窗口变大了),此时效率也就提高了。
类比下,我有很多作业没有完成,此时老师在微信上问我,为啥这么多作业没写呢,此时我先不回复老师,先补一会作业,等作业补的差不多了,再和老师说,我只是哪里哪里这一点作业没写~~ (和上述延时应答机制一个道理)
8.捎带应答
客户端和服务器之间的通信模型通常是”一问一答“的模式,如下图:
上图就是捎带应答机制的完整过程,就是数据可能会合并成一个数据包再进行发送。
9.面向字节流(粘包问题)(映射到写的代码中是否有bug)
比如qq聊天,发送方给接收方连续发送了多条数据(多个应用层数据包),这些数据就积累到接收方的数据缓冲区中挨在一起了,此时接收方在读取数据的时候就难以区分从哪到哪是一个完整的应用层数据包(一条完整的消息),此时就容易读半个包或者一个半的数据包。
其实在前边的代码中也有体现,发送方发送的数据是发到接收方的数据缓冲区中,再由应用程序从缓冲区中读取数据, 那么问题来了,发过了的数据从哪到哪是一条数据呢?
解决方案:
1. 定义分割符,例如写的echoServer服务器(前边的文章),如下图:
2. 约定长度,如:约定数据的前4个字节表示整个数据包的长度。
注:上述两种解决方案都是自定义应用层协议的注意事项!,像xml和json,本质上都是通过分隔符的方式来解决粘包问题的。
10.异常情况:
1. 进程关闭 / 进程崩溃
进程没了,socke是文件,也会随着进程关闭而关闭,虽然进程没了,但是连接还在(连接是由系统内核控制的,和代码关系不大),此时仍然可以继续四次挥手。
2. 主机关机(正常关机)
主机关机了,进程也就关闭了,关机之后先杀死所有的用户进程,也会进行四次挥手,但是不一定能够挥完,挥完,就断开连接了,如果没有挥手完成,比如:对方发来的fin(结束报文)过来了,我还没有来得及ack就关机了,此时就会重传fin,重传几次之后发现对端都没有反馈ack,就尝试重置连接,如果还不可以,就直接断开连接。
3. 主机掉电(突然拔下电源)
瞬间机器就关闭了,此时是来不及进行任何的挥手操作的。
(1)对端是发送方:对端把消息发过来了,但是收不到ack,就超时重传,重传几次无反应,就重置连接,还是不行,直接释放连接。
(2)对端是接收方:对端是无法立即知道我这边是还没有给他回消息还是我的主机掉电了,此时TCP中内置了心跳包(保活机制),虽然对端是接收方,对端也会给发送方定时发送一个心跳包(ping),发送方返回的是一个(pong),如果每个ping都有及时的pong,此时说明当前对端的状态是良好的,如果ping过去之后,没有pong回应,此时就说明对端心跳无了,可能出现问题了。
注:(a)心跳存活机制是周期性发送的,所以如果对端是发送方,不用心跳包,ack比它回 复的及时。
(b)如果心跳没了(对方没有接收到pong的回应),此时就认为是对端挂了。
网络层:IP协议
1.地址管理 | 每个网络上的设备,都要能分配一个为一个地址 |
2.路由选择 | A给B发消息,具体传输数据时走哪条路线 |
1.地址管理,如下图:
此时问题来了;这4个字节最多表示的范围是42亿9千万,这个数字够不够全世界的设备用呢,显然是不够的(有很多电脑,手机,路由器,服务器....),联网的设备有很多,那如何解决这个问题?
解决方案: (但是这两种解决方案都不是根本解决了IP地址不够用的问题,因为并没有增加IP地址的数量,)
1.动态分配IP地址 | 虽然设备多,但不是同一时间都上网,上网就分配一个IP,不上网就不分配。 |
2.NAT机制 | 将IP分类:内网IP(不同局域网中可以重复),外网IP(唯一) |
注:介绍下这里的概念
1. 内网IP:10.* 172.16.* - 172.31.* 192.168.*,此时这几个数字开头的就是内网IP。
局域网就是内网,只要你的电脑连上网,大多数都是处于内网中(局域网中)。
2. 外网IP: 剩下的就是外网IP。
外网IP必须是唯一的,内网IP是可以重复的(在不同的局域网中,如果是同一个局域网内是不可以重复的)
内网设备如果要访问外网,会给他分配一个外网IP,但是这个外网IP不是这个设备独占的,而是这个内网中的所有设备都公用这一个外网IP,(也就是说一个外网IP代表了很多设备)
2. 路由选择(NAT机制 --> 网络地址映射)
两台主机设备同时向服务器发出请求,该走哪条路,该如何区分响应到底是给哪一台主机?
解决方案:如下图;
上图所示;路由器替换了主机的源IP,等到响应传输回来时,它自然是记得替换的是哪一台主机。
注: 1. 现实中是用动态分配IP和NAT机制两种方法结合的方式来解决IP地址不够用的问题。
2.但是这两种解决方案并没有增加IP地址的数量,只是提高了IP的利用率。
3.正是有了NAT机制,才导致我们自己的电脑是在内网中,不能直接被外部直接访问。 (自己的电脑是没有外网IP的)
4.NAT机制是路由器的一个可选功能,路由器可以替换源IP,也可以不替换;并不是所有 的数据经过路由器的转发就一定替换。