传输层
UDP:无连接,不可靠,面向数据报,全双工
TCP:有连接,可靠传输,面向字节流,全双工
端口号
写一个服务器,必须手动指定一个端口号,通过端口来区分当前这个主机上的不同的应用程序,
写一个客户端,客户端在通信的时候也会有一个端口号(代码中感受不到),系统自动分配的.
端口号,固定就是占 2个字节表示的数据范围0->65535一般来说 0 是不用的.
UDP 学习一个协议,最重要的工作就是去理解协议报文格式
校验和
前提: 网络传输中,由于一些外部干扰,就可能会出现数据传输出错的情况
光信号/电信号 磁场,电场,高能离子… 某个地方本来是传输低电平,在干扰下就成了高电平了.
比特翻转
因此, 就需要有办法,能够识别出出错的数据.
校验和,就是这样的一种检查手段~~
校验和,其实本质上也是一个字符串,体积比原始的数据更小,又是通过原始的数据生成的
原始数据相同,得到的校验和就一定相同.
反之,校验和相同,原始数据大概率相同 (理论上会存在不同的情况, 实际的楸率非常低, 可以忽路不计)
如何基于校验和来完成数据校验呢?
1.发送方,把要发送的数据整理好(称为 data1),通过一定的算法,计算出校验和 checksum1
2.发送方把 data1 和 checksum1 一起通过网络发送出去.
3.接收方收到数据, 收到的数据称为 data2(数据可能和 data1 就不一样了),收到数据 cecksum1
4.接收方再根据 data2 重新计算校验和 (按照相同的算法),得到 checksum2
5.对比 checksum1 和 checksum2 是否相同,如果不同, 则认为 data2 和 data1 一定不相同.
如果 checksum1 和 checksum2 相同,则认为 data1 和 data2 大概率是相同的(理论上存在不同的可能性,概率比较低,工程上忽略不计)
通过上面的方式,就能发现数据传输出错~~
校验和是怎么算的??
计算校验和,有很多种算法.
此处 UDP 中使用的是 CRC 算法(循环冗余算法)
把当前要计算校验和的数据,每个字节,都进行累加,把结果保存到这个 两个字节的 变量中,累加过程中如果溢出,也没关系,
如果中间某个数据,出现传输错误,第二次计算的校验和就会和第一次不同~~
CRC 这个算法其实不是特别的靠谱,导致两个不同的数据,得到相同的 crc 校验和的概率比较大.
前一个字节恰好 少1,后一个字节恰好 多 1. (这种情况概率确实不大,但是确实也还是有这方面的风险)
1.定长,无论你原始数据多长,计算得到的 md5,都是固定长度
校验和本身就不应该很长,要不然不方便网络传输
2.分散,给定两个原始数据,哪怕绝大部分内容都一样,只要其中
一个字节不同,得到的 md5 值都会差异很大.
md5 也非常适合作为 hash 算法.
哈希表
哈希表是要把一个 key 通过 hash 函数, 转换成 数组 下标
希望 hash 函数能够做到尽量分散,产生 hash 冲突的概率才会比较低
md5/sha1 算法(魷只介绍 md5)
这里有一系列的公式,来完成 md5 的计算,(咱们不需要考虑公式是啥样的,是一个数学问题),但是咱们需要知道 md5 的特点,
3.不可逆,
给你一个原始数据,计算 md5,非常容易!
给你 md5,还原出原始数据,计算量非常庞大、以至于超出了
现有计算机的算力极限,理论上是不可行的.
md5 也可以应用在一些密码学场累中,
md5 在工作由非常常用,一定要能熟悉这几个基本特点
在 UDP 代码中,都能感知到, UDP 的特点
1.无连接.UDP 协议本身不会存储对端的信息,要在发送数据的时候,显式指定要传输给谁.
// 2、把请求内容构造成 DatagramPacket 对象,发送给服务器
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getyName(serverIp),serverPort);
socket.send(requestPacket);
2.不可靠,代码中体现不出来.
3.面向数据报
4.全双工 通过一个 socket,既可以 send, 又可以 receive
socket.send(requestPacket);
// 3.尝试读取服务器返回的响应
DatagramPacket responsePacket = new DatagramPacket((request.getBytes(),request.getBytes().length,InetAddress.getyName(serverIp),serverPort);
socket.receive(responsePacket);
TCP重中之重
TCP这个协议最大的特点 就是 可靠传输~~
TCP 的相关特性,
可靠传输, 是 TCP 最最核心的特性 (初心)
可靠传输,不是说,发送方把数据能够 100% 的传输给接收方~~(要求太高了)
退而求其次~~
1)发送方发出去数据之后,能够知道接收方是否收到数据
2)一旦发现对方没收到,就可以通过一系列的手段来"补救"
1.确认应答
发送方,把数据发给接收方之后,接收方收到数据就会给发送方返回一个应答报(acknowledge,ack).发送方,如果收到这个应答报文了,就知道自己的数据是否发送成功了
TCP 在此处要完成两个工作:
1.确保应答报文和发出去的数据,能对上号,不要出现歧义
2.确保在出现后发先至的现象时,能够让应用程序这边仍然按照正确的顺序来理解数据
上面的图,还不够严谨,更准确的说,序号不是按照"一条两条"方式来进行编号的,而是按照字节来编号的!!
通过特殊的 ack 数据包,里面携带的"确认序号”告诉发送方, 哪些数据已经被确认收到了
此时发送方,就心中有数了,就知道了自己刚发的数据是到了还是没到,=>可靠传输
TCP 的初心,是为了实现可靠传输 =>达成可靠传输的最核心的机制,就是 确认应答.
如何区分一个数据包是普通的数据,还是 ack 应答数据呢??
在 TCP 报文头中,有一个 ACK 位(标志位),用于指示当前数据包的性质:
这一位为 1,表示当前数据包是一个应答报文,此时该数据包中的"确认序号字段”就能够生效.
这一位为 0,表示当前数据包是一个普通报文,此时数据包中的"确认序号字段”是不生效.
确认应答,是TCP最核心的机制,支持了TCP的可靠传输!!!
TCP是如何保证可靠传输的?(面试题)
正确答案:通过 确认应答 为核心,借助其他机制辅助,最终完成可靠传输。
2.超时重传
确认应答,描述的是一个比较理想的情况
如果网络传输过程中,出现丢包了,乍办??
发送方,势必就无法收到 ACK 了
使用超时重传机制,针对确认应答, 进行补充.
为什么会丢包??
把网络想象成公路.错综复杂的公路网.在公路上就会有很多的收费站~~
网络中,“收费站"可以理解成是一些 路由器/交换机
如果数据包太多了,就会在这些路由器/交换机上出现"堵车”
但是路由器针对"堵车"的处理,往往是比较粗暴的,不会把这些积压的数据包都保存好,而是会把其中的大部分数据包直接给丢弃掉,
真实的网络环境,是错综复杂的,咱们也不知道啥时候,哪个节点上会出现上述“堵车"情况,也不知道啥时候会出现丢包,丢哪个包.…..
非常随机的事件.
这个事情出现概率的大小,取决于网络基础设施…也取决于网络环境,
由于丢包是一个“随机"的事件,因此在上述 tcp 传输过程中,丢包就存在两种情况
1.传输的数据丢了,
2. 返回的 ack 丢了
站在发送方的角度,无法区分这两种情况
无论出现上述哪种情况 发送方都会进行“重新传输”
第一次是丢了,重传一下试试观, 很大概率就能传过去呢~~
重传操作,大幅度的提升了数据的够被传过去的概率~~
重传就是一个很好的丢包下的补救措施了
当引入"可靠性”的时候,是会付出代价的,最明显的代价,是两方面:
1.传输效率
2. 复杂程度
3.连接管理 建立连接 +断开连接
“三次握手”网络部分最经典的面试题***
TCP 的三次捏手,TCP 在建立连接的过程中,需要通信双方一共“打三次招呼”才能够完成连接建立的
三次握手是要解决什么问题??
通过四次握手,是否可行?通过两次握手,是否可行呢??
TCP 初心,是为了实现“可靠传输
进行确认应答 和 超时重传有个大前提,当前的网络环境是基本可用的,通畅的.
如果当前网络已经存在重大故障了,此时,可靠传输,无从谈起,
三次握手核心作用
三次握手核心作用一:投石问路,确认当前网络是否是畅通的
三次握手核心作用二:要让发送方和接收方都能确认自己的发送能力和接收能力均正常.
三次握手核心作用三:让通信双方,在握手过程中,针对一些重要的参数,进行协商
1.确认应答.实现可靠传输的最核心机制.
2.超时重传.等待一定的超时时间,再重新传输,
3.连接管理.如何建立连接(三次握手),如何断开连接(四次挥手)
所谓的网络上的“连接”,通信双方记录了对方的信息,
1)投石问路,验证通信路径是否畅通.
2)验证通信双方的发送能力和接收能力是否正常
3)协商这里的关键参数
像前面的三次握手, ACK 和 第二个 syn 都是内核触发的.同一个时机.可以合并.
这里的四次挥手, ACK 是内核触发的,第二个 FIN 是应用程序执行 close 触发的. 时机不相同,不能合并.
是否意味着,如果我这边代码 close 没写/没执行到,是不是第二个 FIN 就一直发不出去??(有可能的)
如果是正常的四次挥手,“好聚好散”,正常的流程断开的连接.
如果是不正常的挥手 (没有挥完四次),异常的流程断开连接.(也是存在的)
但是,TCP 中还有一个机制,延时应答.(后面再说),能够拖延 ACK 的回应时间,
一旦 ACK 滞后了,就有机会和下一个 FIN 合并在一起了.
断开连接(面试题)
滑动窗口
4.滑动窗口
前三个机制,都是在保证 tcp 的可靠性
提高效率.=>亡羊补牢
TCP 的可靠传输,是会影响传输的效率的.(多出了一些等待 ack 的时间,单位时间内能传输的数据就少了)
滑动窗口,就让可靠传输对性能的影响,更少一些.
TCP 只要引入了可靠性,传输效率是不可能超过 没有可靠性的 UDP 的,
TCP 这里的“效率机制”都是为了让 影响更小, 缩短和 UDP 的差距.
如果通信双方,传输数据的量比较小,也不频繁,就仍然是普通的确认应答和普通的超时重传,
如果通信双方,传输数据量更大,也比较频繁,就会进入到滑动窗口模式,按照快速重传的方式处理
通过滑动窗口的方式传输数据,效率是会提升的.
窗口越大,传输效率就越大.(一份时间,等待 的 ack 更多了,总的等待时间更少了)
滑动窗口, 设置的越大, 越好嘛??
如果传输的速度太快,就可能会使接收方,处理不过来了,此时,接收方也会出现丢包,发送方还得重
TCP 前提是可靠性,可靠性的基础上,再提高传输效率
5.流量控制
站在接收方的角度,反向制约发送方的传输速率,
发送方发送的速率,不应该超过接收方的处理能力,
数据到达 B的系统内核中.
tcp socket 对象上带有接收缓冲区.
A ->B 发的数据,就会先到达 B 的接收缓冲区
B 这边还有应用程序,就会调用 read 这样的方法,把数据从接收缓冲区中读出来,进一步的进行处理.(一旦数据被 read 了,就可以从接收缓冲区删除了) 生产者消费者模型
拥塞控制
6.拥塞控制
流量控制
拥塞控制
都是在限制发送方的发送窗口的大小
最终时机发送的窗口大小,是取 流量控制 和 拥塞控制 中的窗口的较小值.
7.延时应答
A 把数据传给 B,B 就会立即返回 ack 给 A[正常]
也有的时候,A 传输给 B,此时 B 等一会再返回 ack 给 A[延时应答]
本质上也是为了提升传输效率.
发送方的窗口大小,就是传输效率的关键
流量控制这里,就是根据接收缓冲区的剩余空间,来决定发送速率的,如果能够有办法,让这个流量控制得到的窗口更大点,发送速率就更快点(大点的前提,能够让接收方还是能处理过来的)
延时返回 ack,给接收方更多的时间,来读取接收缓冲区的数据
此时接收方读了这个数据之后,缓冲区剩余空间,变大了~~
返回的窗口大小也就更大了
初始情况下,接收缓冲区剩余空间是 10kb,如果立即返回 ack,返回了 10kb 这么大的窗口. 如果延时个 200ms 再返回,这 200ms 的过程中,接收方的应用程序的,又读了 2kb, 此时,返回的 ack,就可以返回 12kb 的窗口了,
8.捎带应答.
在延时应答的基础上,进一步的提高效率,
网络通信中,往往是这种"一问一答"这样通信模型,
9.面向字节流
这里有一个最重要的问题,粘包问题 (不是 tcp 独有的, 而是面向字节流的机制都有类似的情况)
如何解决粘包问题?
核心思路: 通过定义好应用层协议,明确应用层数据包之间的边界
1.引入分隔符.
2.引入长度.
一个简单的例子,使用 \n 作为分隔符.
10.异常情况的处理
如果在使用 tcp 的过程中,出现意外,会如何处理?
1)进程崩溃.进程没了,异常终止了.文件描述符表,也就释放了.相当于调用 socket.close()
此时就会触发 FIN, 对方收到之后,自然就会返回 FIN 和 ACK,这边再进行 ACK(正常的四次挥手断开连接的流程)
TCP 的连接,可以独立于进程存在,(进程没了,TCP 连接不一定没)
2)主机关机(正常流程)
在进行关机的时候,就是会先触发强制终止进程操作.(相当于1)
此时就会触发 FIN,对方收到之后,自然就会返回 FIN 和 ACK.
不仅仅是进程没了,整个系统也可能关闭了,如果在系统关闭之前,对端返回的 ACK 和 FIN 到了,此时系统还是可以返回 ACK,进行正常的四次挥手的,如果系统已经关闭了,ACK 和 FIN 迟到了,无法进行后续ACK 的响应,站在对端的角度,对端以为是自己的,FIN 丢包了,重传 FIN,重传几次都没有响应,自然就会放弃连接.(把持有的对端的信息就删了)
3)主机掉电 (非正常)
此时,是一瞬间的事情,来不及杀进程,也来不及发送 FIN,主机直接就停机了.
站在对端的角度,对端不一定知道这个事情咋搞~~
1、如果对端是在发送数据(接收方掉电),发送的數据就会一直等待 ack.触发超时重传.触发 TCP 连接重置功能,发起“复位报文段” (RST这个复位报文段,发过去之后)
如果 复位报文段 发过去之后,也没有效果,此时就会释放连接了
2、如果对端是在接收数据 (发送方掉电),对端还在等待数据到达.…….,等了半天没消息,此时其实无法区分,是对端没法消息还是对方挂了
TCP 中提供了 心跳包 机制.(形象的比喻)
接收方也会周期性的给发送方发起一个特殊的,不携带业务数据的数据包,并且期望对方返回一个应答
如果对方没有应答,并且重复了多次之后,仍然没有,就视为对方挂了,此时就可以单方面释放连接了.
4)网线断开
网线断开,和刚才的主机掉电非常类似的,
TCP 的心跳机制:咱们在日常开发中,经常见到这种心跳机制.(尤其是在分布式系统中)
TCP和UDP之间的对比
经典面试题: 如何基于 UDP 实现可靠传输??
这个问题本质上是考察 TCP !!
写代码,在应用程序这里把可靠传输给做出来.TCP 是怎么可靠传输的,照抄呗~~
1)确认应答
2)引入序号 确认序号
3)超时重传
4)滑动窗口
……