网络原理 - 传输层(二十一)

传输层

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)滑动窗口
……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值