物理层
最主要的功能:传输比特流。利用传输介质为数据链路层提供物理连接,实现比特流的传输。让上一层的数据链路层传送数据是尽可能屏蔽掉传输介质和物理设备的差异。
数据链路层
在物理层提供的比特流的基础上,通过差错控制、流量控制等方法,将物理层的比特流封装成数据帧,传送到上一层。同样将这一层的数据帧插装成比特流转发到物理层。
网络层
数据链路层的数据在这一层转化成数据包,通过路径选择、ip寻址,基于IP地址进行路由转发,将信息从一个设备传送到另一个网络设备。
传输层
提供可靠的端到端的差错控制与流量控制,保证报文的正确传输。
会话层
建立和管理应用程序之间的通信,可以检查两个app之间的对话是否可以对接
表示层
数据格式化、加密解密。
应用层
直接向用户提供网络服务,完成用户在网络上的各种操作。
实际上OSI模型只是一个概念,并没有落地,在后期的实际组件网络中,是以TCP/IP五层模型来实现的
应用层
复测应用程序之间的沟通。如简单电子邮件传输(SMTP) 、文件传输协议(FTP)、网络远程访问协议(Telnet)
传输层
负责两台主机间的数据传输。如TCP、UDP协议,能够保证数据可靠的从源主机发送到目的主机
网络层
负责IP寻址和路由选择。 在IP协议中,通过IP地址来表示标识一台主机,并通过路由表的方式规划两台主机之间的数据传输线路(路由)。
数据链路层
负责设备之间数据帧的传送和识别。实现数据帧与比特流的转换
物理层
负责物理介质的传输设备,传输比特流。
UDP 报头一共8个字节,4个字段
(1) 源端口号,2个字节,16位
(2) 目的端口号 ,2个字节,16位
(3) UDP数据报长度,2个字节,16位,
能表示0-65535 的数据,单位是字节,所以UDP数据报最大长度是 64KB,如果报文长度大于64KB,可能丢失数据。
(4) 校验和
传输的都是比特流0、1,可能受到干扰发生比特翻转,数据发生错误。校验和来校验数据。
几种校验和的算法
CRC 循环冗余校验
简单介绍一下
发送数据之前,按照字节取出数据再进行累加得到一个CRC校验和,发送的时候发送数据包+CRC校验和,接收方拿到数据后,根据数据在计算一遍CRC校验和,如果和收到的CRC一致,说明数据大概率没有问题。
md5
也是一种算法,本质上是一个非对称的哈希算法
特性:
1.定长:无论输入的字符串是多长,得到的md5值都是固定长度
2.分散:只要输入的字符串,哪怕只变化了一点点,得到的md5 值都会变化巨大
3.不可逆:给定原串,很容易得到md5值,但是给定md5 值,理论上无法恢复成原串的。
(1) 源端口号 2个字节 16位
(2) 目的端口号 2个字节 16位
(3) 序号和确认序号 都是32位,4个字节
他俩的作用是保证应答机制的可靠性,就是发送方连续发送了几个数据,接收方也回了多个应答报文,能用序号和确认序号给应答报文和发送的数据对应上。
(4) 4位首部长度 它能够表示0-15 ,但是呢,头部长度远远大于15个字节,所以它的单位是4个字节,首部长度是15就意味着头部长度是 60个字节,首部长度最大是60个字节
首部长度划分了 header 和 payload 的分界线
(5)保留6位
现在还没啥用,以后可能有用,就先留着。
(6)
发送方发送数据给接收方了,接收方立刻回应一个应答报文。这个是TCP 保证可靠性的核心
如果A连续发了两条信息,B也要回两条信息,怎么保证应答和发送是对应的呢?
什么时候重传?
1.A发送给B数据,因为网络堵塞,数据丢了,A一直没有收到B的应答,不知道是否发送成功给B.
2.A给B发送数据,B接收到了,B给A发送应答报文,这个应答报文丢了,所以A就也不知道到底发送成功了没有。
所以这种情况怎么解决?
等待一段时间后,重发一次,还没有收到应答,再次重发,每重发一次等待的时间越长,(让重发的频率降低),累积到一定的重发次数,还是失败的话,网络异常连接关闭。
有两台主机想要建立连接
A发送了一个SYN=1的报文给B,尝试建立连接。
B收到请求后,立刻发送一个ACK=1的报文确认应答,同时第一时间发送一个SYN为1的报文,也尝试与A建立连接。因为都是第一时间发送这个报文,为了减小开销,就合成一步发送SYN 、ACK都为1 的报文给A
A收到B的应答ACK和SYN后,立刻返回一个ACK=1 的报文确认应答。
至此,两台主机的TCP就连接完成。
同上
TCP三次握手的目的是什么?
1.投石问路。通过三次握手的过程,来确认发送方与接收方的发送能力以及接受能力都是正常的。
2.协商参数。通过三次握手,让发送方和接收方在正式连接之前先通通气,选择一些传输中合适的参数,比如:TCP传输数据的序号从几开始。
为什么TCP是三次握手?能不能是两次握手?能不能是四次握手
为啥是三次握手,说明:
TCP建立连接之前,发送方和接收方不知道对方的接收发送能力,也不知道自己的发送接收能力。
A要知道自己收发正常,也要知道B收发正常。
B要知道自己收发正常,也要知道B收发正常。
结合图片进行分析,
第一次握手:A尝试与B建立连接
B知道了 A的发送正常,B的接收正常
第二次握手:B确认引发、尝试与A连接
A知道了 B的发送能力、接收能力,A的发送能力、接收能力都正常,A对于双方的情况都知道了,但是B他不知道B自身的发送能力是否正常
第三次握手:A发送确认应答报文
这个时候,B就知道了B的发送正常,A的接收也正常。
同时A、B双方都知根知底了,都知道对方和自身接收发送能力是否正常了,所以完成连接。
两次握手行不行?
你就和面试官说一下前两次握手的细节过程,给他走一遍,看看发送方到底知道了啥,接收方到底知道了啥,只有发送和接收方都啥都知道,才能建立可靠的连接。
不行,此时只有发送方对自己和对方的收发能力都知道了,但是接收方信息不全,不知道对方的接收和自己的发送是否正常,所以如果两次握手建立连接,那么这个连接就会有问题。
四次握手行不行?
理论上是可以的,就是把第二次握手,发送的那个syn=1 ack=1 的报文拆成两次发,但是没必要反而会增大开销,如果把这个一个报文分成两次发,那么就要在经历层层封装报头,解析报文等等等操作开销很大,所以因为第一次握手直接同时触发了确认应答和同步报文,合成一个报文减小开销提高效率,理所应当。
这个过程一定要记住客户端与服务器端的各个状态
第一次挥手
客户端----服务器端
客户端想要尝试断开连接,发送了一个FIN=1 的报文,此时客户端进入fin-wait1 状态
fin_wait 1 这个状态是等待自己的fin报文是否发送成功,等待接收回应
第二次挥手
服务器端收到客户端的请求断开连接的请求,确认应答,回复一个ACK报文
服务器端进入close_wait 状态,等待执行 自身断开连接的请求
close_wait状态 服务器收到FIN之后进入到的状态,等待用户代码执行 close关闭连接操作,因为要等待关闭,所以 叫 close _ wait
(服务器端请求断开是用户控制的,比如网络编程中,我们在socekt.close()之前 执行thread.sleep,那么服务器就得等待,时间到了才能执行发送fin报文)
此时客户端接收到了ack,进入到了fin_wait2 状态
fin_wait2 状态是等待接收服务器端的fin报文
第三次挥手
服务器可以执行 断开连接的操作,给客户端发送一个fin报文,此时进入到last_ack 状态,等待客户端回应ack
last_ack 服务器端在发送完fin之后,等待客户端回应一个ack的状态
第四次挥手
客户端接收到了服务器端发送到fin报文,确认应答回应一个ack报文
客户端在回复的同时立刻进入到time_waiting 状态,这个状态是为了处理最后一个ack丢失。
随后如果最后ack正常发到,如果客户端没有接收到服务器重传的fin,那么此时销毁连接,tcp连接关闭。
time _wait 状态(重点)
这个状态就是为了处理最后一个ack丢包,就是如果A收到fin并回复一个ack,就直接销毁连接了,而不是进入time _wait状态,那么可能是因为丢包,最后B 没接收到ack就销毁了,最后一个ack就没接收到。因为连接销毁也没办法重传ack了。
所以进入到time_wait 状态等待一段时间,如果一定时间内客户端没有收到服务器重传的fin发过去,才会真正的销毁连接。
time_wait 会等待多长时间?
2MSL,MSL是报文最大生存时间
1MSL是主机A和主机B之间的一次最大通信时间,就是发送方发送数据给接收方,接收方处理后又会给对方相应,一来一回需要等待2倍的时间。
2MSL是从客户端收到服务器的fin发送ack开始计时的,如果在time-wait 时间内,客户端的ack没有传输到服务器,那么服务器就会重传的fin报文,客户端重新发送ack。
tcp挥手的过程什么时候会丢包?
1.第一个fin丢了,A收不到ack,就会重传fin
2.第一个ack丢了,A收不到ack,就会重传fin
3.第二个fin丢了,B收不到ack,就会重传fin
4.第二个ack丢了,B收不到ack,就会重传fin
为什么发送ack 和 fin 的报文不能合并发送呢?
对于服务器来说,ack和fin的触发时机是不一样的
1.服务器收到客户端的fin,就会立刻出发ack,这是内核完成的
2.服务器发送fin报文是用户代码控制的(用户的代码出现了socket.close才会触发)
四次挥手一定是四次吗?能不能是三次?
有可能是三次,虽然ack和fin是不同时机触发的,但是在延时应答和捎带应答的情况下是有可能合并在一起的。
四次挥手一定会执行吗?
也不一定,四次挥手是一个TCP正常断开的流程,但是又的时候,TCP连接会异常断开(网线直接拔了)
TCP不仅要考虑可靠性,还要尽可能的提高效率问题。
就是说因为TCP有一个确认应答的特性,对于每个发送的数据段,收到之后都会回复一个ack报文应答,但是这有一个缺点,性能较差。
一发一收的方式处理数据,效率很低。
所以TCP是一次发一波数据,一次接收多个ack,提高效率。
这里的提高效率可以用一个例子来理解,
一发一收
我去小吃街去买饭,我想买香肠、包子、饺子,一发一收就是我去包子摊给老板说买包子,等他做好了我再去香肠摊给老板说卖香肠,等他做好了再去饺子摊,等他做好了就买完了。最后我们所需的时间就是所有小吃摊做饭的时间
多发多收
我去小吃街去买饭,我想买香肠、包子、饺子,我先去包子摊说我要买包子,说完直接去饺子摊,给老板说买饺子,说完立刻去香肠摊给老板说要香肠,同时等他们做完,最后我们所需要等待的时间是做饭最长的那个摊的时间
滑动窗口就是多发多收的机制,但是限制了发送数据的大小
而什么是滑动窗口,滑动窗口是一次发送数据的最大值,比如说窗口大小4000,一次只能发送11000,10012000,20013000,30014000,这是数据发送的范围,然后接收到1001,2001,3001,4001 的ack,说明已经全部发送成功了。
在滑动窗口的机制下,如果发生了丢包,怎么重传呢?
1.丢的是ack。
如果丢的是ack,那么无所谓,我们通过序号和确认序号来对应每条请求和确认应答的,确认序号的作用就是说明前面的数据都接收到了,如果后面数据的ack都接收到了,那么前面数据的ack接不接收都无所谓了。
2.丢的是发送的数据
如果丢的是发送的数据的话,发了11000,10012000,20013000这几个数据,11000丢失的话,虽然后面的数据服务器接收到了,但是服务器这边一直没有1001的数据,那么接收端会一直索要确认序号是1001的报文,提醒发送端重发,发送端接收到3次后,就会重新发送这块数据1~1000
流量控制是对滑动窗口的一种补充~
窗口大小是决定了传输的效率,窗口越大效率越高但是不可靠了,窗口越小效率降低但是可靠。
所以通过流量控制来保证效率和可靠性的均衡。
滑动窗口的值能不能无限大?
如果无限大的话,就很不好控制,我们应答机制就是一个请求一个回应,如果一次全部发送完,就是不等ack就一顿发,就完全没有可靠性而言了,我们是靠ack来保证可靠性的。
|========================================
怎么控制流量?
滑动窗口的大小就是 接收方缓冲区大小的字节数
发送数据与接收数据很像生产者消费者模型,阻塞队列就相当于这个接收方的缓冲空间。如果一下子发送数据的速度太快了同时窗口很大,但是接收方读取数据的速度很慢,那么接收方一下子就处理不了了。
所以发送方每过一段时间发一个窗口探测包,问一问接收方缓冲剩余空间还有多少空间,发送一个数据包询问tcp首部窗口大小,再回一个确认报文ack得知还有多少空间。然后再根据剩余空间的大小决定发送数据的速度以及多少。
如果窗口大小为0,停止发送数据,等到滑动窗口有空间了再次传输,其中怎么得知是否有空间就是通过窗口探测包来确定的。
TCP首部窗口大小字段最多是64KB,那么传输更大的数据发现效率太慢怎么办?
TCP首部滑动窗口只有16位,表示0-65525,单位是字节,所以最多表示滑动窗口的大小是64kb,但是真实要传输的数据确实很大的,64kb的滑动窗口确实太小了,所以在tcp首部选项字段中有一个窗口扩大因子M,实际的窗口大小是 (16位窗口大小左移M位)
站在另一个角度来控制滑动窗口的大小的方式
先用木桶原理来说明就是在传输数据的时候发送方和接收方要经过很多网络路由设备,如果只根据接收方的接受能力,比如接收方处理数据很快,那么发送方就发的快这样是很不合理的,尽管接收方处理的很快,但是只要中间过程有一个设备处理的很慢,那么整体就很慢。
所以用拥塞控制的方式来解决这种问题
拥塞控制确认滑动窗口的大小
为了不发生上面的情况,发送方先放松一个比较小的窗口来传输数据,看看是否丢包。
如果不丢包,说明网络通畅,那么就逐渐加大发送速率
如果丢包,说明网路堵塞,那么就立即降低发送速率。
经过多次实验,能够得到一个具体的合理的能够传输数据的一个窗口大小。
最终的窗口大小是(流量控制得到的窗口大小)与 (拥塞控制得到的窗口大小) 两者之间的最小值
拥塞控制计算窗口大小的方式(重要重要重要!!!)
我认为可以先举例子。
你和对象刚开始认识的时候,很有新鲜感,所以感情上升的很快(指数级上升),随着时间的推移,感情上升的就比较平稳了,当发生了矛盾,感情直接调入低谷。 和好之后感情又从低谷上升也很快(但是没有第一次快),然后上升到程度,又开始平稳,又遇到矛盾重复此过程。
拥塞控制计算滑动窗口的大小的大概流程就是这么个过程
具体我们来说一下,在tcp中计算拥塞控制的滑动窗口怎么计算。
慢启动
窗口大小从1开始,二倍增长
拥塞避免
达到一定的阈值ssthresh,此时每次窗口大小cnwd+1
网络超时:
如果这个窗口的数据发送过去,造成了网络超时,此时窗口大小cnwd从1开始慢启动,此时阈值为之前网络超时cnwd/2,然后达到阈值拥塞避免,逐渐+1.
得到失序报文段
就是虽然没有造成网络超时,但是我们接收方得到的数据不是完整的,数据报丢失了或者发生错误了,所以此时的窗口大小也是不可取的。发送方接收到三个重复确认后,进行快恢复。窗口大小又从当前窗口的一半重新开始拥塞避免,逐渐+1.
以后每次都是这样的过程,把这个具体的窗口大小给更加精细化,到最后算出一个大小来。
之前我们通过流量控制、拥塞控制得到了一个具体的窗口大小
但是如果接收到数据立刻返回ack的话,那么此时返回的窗口可能较小。
假如接收缓冲区为1M,一次收到500k的数据,如果立刻应答,返回的窗口就是500k,但是实际上接收端处理数据很快,如果在等一小会,窗口里的数据就全部处理完了,所以我们可以在保证网络不阻塞,数据不丢失的前提下,延时发送ack,此时返回的窗口大小就更大,那么数据吞吐量就越大,效率就越高。
在延时应答的基础上,有了捎带应答,提高效率
啥叫捎带应答?
咱通俗的说,就是因为延时应答,导致原本发送两个报文,但是因为延时让这两个报文在同一时刻发送,那么就可以捎带应答,合成一个报文发送,提高效率
原本内核态的ack因为延时应答,导致和用户态的响应报文一块发送,合成一个报文返回给客户端,这就叫做捎带应答。
通过这种方式,很多发送都能合二为一。
四次挥手也能变成三次!!
TCp数据包是面向字节流的,所以接收方拿到数据后,放到缓冲区,缓冲区全是字节
应用程序从缓冲区读取数据的时候,就不知道从哪到哪是一个完整的数据包了。此时应用程序只能看到一个一个的字节,然后就整混了.
这就是粘包问题
通过设计一个合理的应用层协议解决
1.给应用层数据设置结束符、分隔符
每个数据都加上一个分号,然后应用层读取的时候,如果读到分号,就知道前面的数据是一个完整的报文
2.给应用层设定一个长度
给应用层数据中加上一个读取的长度,这个长度int占4个字节
接收方先读前4个字节,就知道一个完整数据报的内容到哪里了,然后把之后的内容读去这个长度
然后再读4个字节,就知道这个数据包的长度,然后读取这个数据报…
进程终止
进程终止后会释放PCB,也会释放对应的文件描述符,此时也会触发TCP四次挥手,进程终止相当于socekt.close()
机器重启
机器重启也是先杀进程,就是TCP会先四次挥手,只不过可能还没挥完电脑就关闭了
机器断电/网线断开
突发情况,机器来不及任何操作
要分成两种情况
1.断电的是接收方
发送方还是正常运行,正常发送数据,只不过一直收不到ack,那么此时超时重传,重传几次后还没反应,就会重置连接,将复位报文段RST=1 发送给接收端,要是还没反应,就把连接销毁了。
2.断电的是发送方
发送方断电了,接收方还正常运行,发送方发送不了数据了,接收方不知道发送方是挂了还是暂时没发数据。所以接收方采用心跳包机制,每过一段时间发送一个PING包,如果对方回复一个PONG包说明还有连接 ,如果PING包发过去,没反应,重试几次还没反应,认为发送方挂了。
把场景引导TCP发生异常的情况
发送方突然挂了,来不及四次挥手。
然后。。。。
说一下tcp的各个特点就ok了
1.实现确认应答,tcp在内核完成ack,我们可以在应用层发送一个ack应答数据包
2.实现序号与确认序号,确认序号保证之前的数据都收到了
3.实现超时重传,超过一定时间就重新发送,每次等待的时间更差高一点,降低重传的次数
4.实现连接管理(实现三次握手、四次挥手的过程)
5.实现滑动窗口,一次实现多发多收的效果,提高效率。
6.实现流量控制/拥塞控制
流量控制是通过每次返回带有缓冲区的剩余空间大小的数据包给发送方,然后发送方决定滑动窗口的大小
拥塞控制是发送方先发送一个滑动窗口很小的一个报探探路,看网路是否拥塞,经过多次实验,得到一个窗口大小。
7.实现延时应答、捎带应答、心跳包机制
1.需要可靠性,首选TCP
2.发送的单个数据报较长(超过64kb),首选tcp
3.需要效率,首选udp. 因为tcp为了所谓的可靠性牺牲了很多效率,尽管使用了滑动窗口、拥塞控制、捎带应答等提高效率的手段,但是还是没udp快
需要效率的场景:机房内部的主机间通信
(1)网络环境简单,带宽充裕,丢包可能性不大
(2)机房内部的通信,往往数据量更大,更需要速度,需要效率
4.如果需要广播(一份数据同时发给多个主机),优先考虑udp
tcp只能在应用层程序,通过多个连接,通过轮询的方式给每个主机返送数据(伪广播)
1.tcp是可靠的,用确认应答超时重传等来保证可靠性,udp是不可靠的
2.tcp有连接,udp无连接
3.tcp面向字节流,udp面向数据报
4.tcp全双的可靠信道,udp是不可靠信道
5.udp首部只有8个字节,udp数据包长度只有64kb。tcp首部最小长度是20个字节,可能还有选项4n个字节。tcp的有一个首部字段4位首部长度,最大表示15,单位是4个字节,如果头部最大长度就是60个字节