unity网络开发理论
前言
计算机网络部分
OSI模型
五层模型 | 作用 |
---|---|
应用层 | 为应用程序提供交互服务。在互联网中的应用层协议很多,如域名系统DNS、HTTP协议、SMTP协议等。 |
传输层 | 负责向两台主机进程之间的通信提供数据传输服务。传输层的协议主要有传输控制协议TCP和用户数据协议UDP。 |
网络层 | 选择合适的路由和交换结点,确保数据及时传送。主要包括IP协议。 |
数据链路层 | 在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。 |
物理层 | 实现相邻节点间比特流的透明传输,尽可能屏蔽传输介质和物理设备的差异。 |
层级 | 名称 | 描述 |
---|---|---|
7 | 应用层 | 计算机用户、应用程序与网络之间的接口,提供网络服务及应用所需的监督、管理和服务等协议,负责协调各个应用程序间的工作。 |
6 | 表示层 | 对来自应用层的命令和数据进行解释,处理用户信息的表示问题,如编码、数据格式转换和加密解密,并传送给会话层。 |
5 | 会话层 | 用户应用程序和网络之间的接口,组织和协调两个会话进程之间的通信,并对数据交换进行管理。 |
4 | 传输层 | 提供可靠的端到端的差错和流量控制,保证报文的正确传输,屏蔽下层数据通信的细节,向高层透明地传送报文。常见的协议有TCP、SPX和NetBIOS/NetBEUI。 |
3 | 网络层 | OSI参考模型中最复杂的一层,控制数据链路层与传输层之间的信息转发,建立、维持和终止网络的连接,负责路由选择、数据包转换和跨网络节点间的通信。 |
2 | 数据链路层 | 将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路,通过差错控制、流量控制方法提供可靠的数据传输。 |
1 | 物理层 | 利用传输介质为数据链路层提供物理连接,实现比特流的透明传输,尽可能屏蔽掉传输介质和物理设备的差异。 |
TCP与UDP的区别
区别 | UDP | TCP |
---|---|---|
是否连接 | 不连接 | 面向连接 |
是否可靠 | 不可靠 | 可靠传输(传输过程中会丢失,但会重发)使用流量控制和拥塞控制 |
连接对象个数 | 支持一对一,一对多,多对一,多对多交互通信。 | 仅支持一对一通信。 |
传输方式 | 面向报文 | 面向字节流 |
数据边界 | 保存数据边界 | 不保存数据边界 |
速度 | 速度快 | 速度慢 |
发送消耗 | 轻量级(因为 UDP 传输的信息中不承担任何间接创造连接,保证交货或秩序的信息。这也反应在包头大小。) | 重量级 |
首部开销 | 首部开销小,仅8个字节 | 首部开销大,最小20字节,最大60字节。 |
有序性 | 不提供有序性的保证 | TCP 保证了消息的有序性,即使到达客户端顺序不同,TCP 也会排序。 |
应用场景 | IP电话,视频会议,直播,以及FPS竞技类的使用UDP帧同步。 | 要求可靠传输的应用例如文件传输,以及MMO类的TCP状态同步。 |
TCP三握四挥
为什么TCP需要三次握手,四次挥手?_tcp 为什么需要三次握手和四次挥手?-CSDN博客
三握
- 首先 Client 端发送连接请求报文。
- Server 段接收链接后回复ACK 报文,并为这次连接分配资源。
- Client 端接收到 ACK 报文后也向 Server 段发生 ACK 报文,并分配资源,这样 TCP 连接就建立了。
小结:
三次握手的关键是要确认对方收到了自己的数据包,这个目标就是通过 “确认号(Ack)”字段实现的。计算机会记录下自己发送的数据包序号 Seq,待收到对方的数据包后,检测 “确认号(Ack)字段”,看 Ack = Seq + 1 是否成立,如果成立说明对方 正确收到了自己的数据包。
为什么要三次握手
- 如果只有一次握手,Client不能确定与Server的单向连接,更加不能确定Server与Client单向连接;
- 如果只有两次握手,Client确定与Server的单向连接,但是Sevrer不能确定与Client的单向连接;
- 只有三次握手,Client与Server才能相互确认双向连接,实现双方的数据传输。
四挥
Client 发送一个 FIN,用来关闭 Client 到 Server 的数据传送, Client 进行 FIN_WAIT_1 状态。
Server 收到FIN 后,发送一个 ACK 到 Client,Server 进入CLOSE_WAIT 状态,Client收到ACK,进入FIN_WAIT_2 状态。
Server 发送一个 FIN,用来关闭 Server 到Client 的数据传送,Server 进入 LAST_ACK 状态。
Client 收到 FIN 后,Client 进入 TIM_WAIT 状态,发送 ACK 给Server,Server 进入 CLOSED 状态,TIME-WAIT一般为2个MSL(报文最长寿命),如果在这个时间段内没有收到服务端的超时重发,说明客户端发过去的ACK服务端收到了并且已经关闭连接,客户端才进入CLOSED状态。完成四次握手。
为什么要四次挥手
服务端在收到客户端的释放报文时,可能自己的数据报还没有发完,所以不会直接返回FIN+ACK,而只先返回一个ACK,表示自己收到了客户端的释放请求(第二次挥手)。等到服务端报文发完以后,在返回FIN(第三次挥手)。
那么,我们是否可以在服务器端数据传送完成后,再返回FIN+ACK呢?中间就可以省略一次ACK了?(省略第二次挥手)
试想一下,如果服务端还有很多数据需要传送,耗时长,客户端在发送释放报文后,一直没有收到反馈,那么他会认为服务端没有收到我的FIN,因此就会不停的重发FIN。(第一次挥手)
所以最好的办法就是,客户端发送FIN,服务端回复ACK,表示我已经收到了,但是我在忙,你等等,我处理完成后联系你。服务端数据传送完成后,发送FIN给客户端,客户端再回复ACK。
四次挥手中为什么等待2MSL?
确保A发送的最后一个ACK报文段能够到达B是非常重要的。这个ACK报文段有可能丢失,如果B收不到这个确认报文,就会超时重传连接释放报文段。然后A可以在2MSL(Maximum Segment Lifetime,最大报文段寿命)时间内收到这个重传的连接释放报文段。接着A会重传一次确认报文,并重新启动2MSL计时器。最后,A和B都进入到CLOSED状态。
如果A在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,那么它可能无法收到B重传的连接释放报文段。这样,A就不会再发送一次确认报文段,导致B无法正常进入到CLOSED状态。
为了防止已失效的连接请求报文段出现在本连接中,A在发送完最后一个ACK报文段后,需要再经过2MSL的时间。这样可以确保这个连接所产生的所有报文段都从网络中消失,从而避免在下一个新的连接中出现旧的连接请求报文段。
简单来说
- 用来重发可能丢失(第四次挥手)的ACK报文
- 避免服务器有了新的数据需要发送给客户端。
TCP 重传、滑动窗口、流量控制、拥塞控制
1. TCP 重传
- 定义:当发送方未收到接收方的确认(ACK)时,会重新发送数据包。
- 触发条件:
- 超时重传:发送方在预定时间内未收到ACK。
- 快速重传:收到三个重复ACK时,立即重传丢失的包。
- 作用:确保数据可靠到达,即使出现丢包。
2. 滑动窗口
- 定义:发送方和接收方通过窗口机制控制数据流量,窗口大小决定了一次能发送的数据量。
- 工作原理:
- 发送窗口:发送方可以连续发送的数据范围。
- 接收窗口:接收方能够处理的数据范围。
- 滑动:随着ACK的到达,窗口向前滑动,允许发送新数据。
- 作用:提高网络利用率,允许连续发送多个数据包。
3. 流量控制
- 定义:通过调节发送速率,防止接收方因缓冲区不足而丢包。
- 实现方式:
- 接收窗口:接收方通过ACK告知发送方其剩余缓冲区大小。
- 零窗口:当接收方缓冲区满时,通知发送方暂停发送。
- 作用:避免接收方过载,确保数据传输的稳定性。
4. 拥塞控制
- 定义:通过调节发送速率,防止网络过载。
- 主要算法:
- 慢启动:初始时指数增长发送窗口,探测网络容量。
- 拥塞避免:窗口线性增长,避免过快引发拥塞。
- 快速重传和快速恢复:通过重复ACK检测丢包,快速恢复传输。
- 作用:防止网络拥塞,保持网络稳定。
总结
- TCP 重传:确保数据可靠到达。
- 滑动窗口:提高传输效率。
- 流量控制:防止接收方过载。
- 拥塞控制:防止网络拥塞。
这些机制共同保障了TCP的可靠性和高效性。
Socket粘包
概念
多个独立的数据包黏连在一起,形成了一个连续的数据流。
考虑时机
如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题。
如果发送的数据无结构,比如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包。
如果双方建立连接,需要在连接后一段时间内发送不同结构数据,如连接后,有好几种结构:
- ”good good study”
- ”day day up”
那这样的话,如果发送方连续发送这个两个包出去,接收方一次接收可能会是”good good studyday day up” 这样接收方就傻了,因为协议没有规定这么奇怪的字符串,所以要把它分包处理,至于怎么分也需要双方组织一个比较好的包结构,所以一般可能会在头加一个数据长度之类的包,以确保接收。
产生原因
发送端:发送端需要等待缓冲区满才进行发送,这种机制有时会导致多个消息被合并成一个大的数据包发送出去,造成粘包现象(即发送端出现粘包)。
接收端:接收端在接收数据时,若未能及时读取缓冲区中的数据,多个数据包可能会在缓冲区中累积,导致一次性接收多个数据包,出现粘包现象(即接收端出现粘包)。
如何解决
- 自定义协议法
由于缓冲区过大可能导致粘包,因此,在发送/接收消息时,可以先将消息的长度作为消息头部的一部分发送出去。这样,接收方在接收到消息长度后,就可以根据这个长度来动态定义缓冲区的大小,从而避免粘包问题。这种方法通过自定义协议来实现,是实际应用中最为常用的解决方案。 - 特殊字符标记法
对于发送的数据,可以在每条消息的首尾加上特定的字符作为标记。然后,将所有消息拼接成一个字符串发送出去。接收方在接收到这个字符串后,通过识别这些特殊标记来分割字符串,从而截取出每条独立的消息。但这种方法仅适用于数据量较小的情况,因为在大数据量传输时,特殊字符的出现可能会变得频繁,导致解析复杂度和错误率增加。
- 消息定长。发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
- 设置消息边界。服务端从网络流中按消息边界分离出消息内容。在包尾增加回车换行符进行分割,例如FTP协议。
- 将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段。
- 更复杂的应用层协议。
Socket的封包、拆包
为什么需要封包、拆包
为什么基于TCP的通信程序需要封包、拆包?
答:TCP是流协议,所谓流,就是没有界限的一串数据。但是程序中却有多种不同的数据包,那就很可能会出现如上所说的粘包问题,所以就需要在发送端封包,在接收端拆包。
如何封包、拆包?
答:封包就是给一段数据加上包头或者包尾。比如说我们上面为解决粘包所使用的两种方法,其实就是封包与拆包的具体实现。
序列化
序列化 简单理解成把对象转换为容易传输的格式的过程。
⽐如,可以序列化⼀个对象,然后使⽤HTTP通过Internet在客户端和服务器端之间传输该对象
反序列化 :将已经序列化过后的数据恢复成原先对象的过程。
1. 序列化(Serialization)
- 定义:将对象或数据结构转换为可存储或传输的格式(如字节流、JSON、XML等)。
- 目的:
- 数据持久化:将对象保存到文件或数据库中。
- 网络传输:将对象转换为适合网络传输的格式。
- 常见格式:
- 二进制格式:如Protocol Buffers、Java的
Serializable
。 - 文本格式:如JSON、XML、YAML。
- 二进制格式:如Protocol Buffers、Java的
- 特点:
- 平台无关性:序列化后的数据可以在不同系统间传输。
- 可逆性:序列化后的数据可以通过反序列化恢复为原始对象。
2. 反序列化(Deserialization)
- 定义:将序列化后的数据重新转换为原始对象或数据结构。
- 目的:
- 恢复数据:从文件或数据库中读取并重建对象。
- 接收数据:从网络传输中接收并重建对象。
- 过程:
- 读取序列化数据。
- 根据数据格式和结构,重建原始对象。
- 注意事项:
- 数据一致性:确保序列化和反序列化的数据格式一致。
- 安全性:反序列化可能引入安全风险(如反序列化攻击),需验证数据来源。
序列化方案 | 描述 | 优点 | 缺点 |
---|---|---|---|
XML | 可扩展标记语言,通用且重量级的数据交换格式,以文本结构存储 | 1. 格式统一,标准 | 1. 数据冗余,比JSON体积大 |
2. 容易与其他系统远程交互 | 2. 解析效率较低 | ||
3. 数据共享方便 | |||
JSON | 通用且轻量级的数据交换格式,以文本结构存储 | 1. 简单易用,开发成本低 | 1. 体积大,影响高并发 |
2. 跨语言 | 2. 无版本检查,需自己做兼容 | ||
3. 轻量级数据交换 | 3. 片段创建和验证过程复杂 | ||
4. 非冗长性(对比XML) | 4. 缺乏命名空间导致信息混合 | ||
5. 没有XML那么通用和深入人心 | |||
Protobuf | Google的数据交换格式,以二进制结构存储 | 1. 跨平台多语言,可自定义数据结构 | 1. 可读性差,抓包后的数据难以解读 |
2. 字段编号,解决向后兼容问题 | 2. 对象冗余,生成的类较大 | ||
3. 序列化体积小,适合网络传输 | 3. 默认不具备动态特性 | ||
4. 序列化/反序列化速度快 | |||
5. 自动化生成代码,简单易用 | |||
6. 二进制消息,效率高 | |||
7. Netty等框架集成,提高开发效率 |
方案比较 | JSON | XML | Protobuf |
---|---|---|---|
流行程度 | Web项目中最流行 | WebService中广泛应用 | 后起之秀,高性能场景适用 |
浏览器支持 | 非常好,有内建函数支持 | 较好 | 不涉及浏览器支持 |
数据冗余 | 较少,键值对方式 | 较多,需要成对闭合标签 | 较少,二进制格式 |
可读性 | 较好 | 较好 | 差,需反序列化后可读 |
性能 | 适中 | 较低 | 高,二进制格式 |
适用场景 | Web项目,轻量级数据交换 | WebService,数据共享 | 高性能,响应速度有要求的数据传输 |
网络抖动
什么是网络抖动
- 网络抖动是用来描述网络延迟变化的一个术语。在网络中,当数据分组(或称为数据包)通过连接传输时,由于各种原因,如网络拥塞、设备性能等,导致这些分组到达接收端的时间存在差异。这种延迟的变化程度就被称为网络抖动。
- 简单来说,网络抖动是网络延时变化的一个量化指标,它代表了最大延迟与最小延迟之间的时间差。例如,
如果最大延迟是20毫秒,最小延迟是5毫秒,那么网络抖动就是15毫秒
。抖动是衡量网络稳定性的一个重要参数。
造成网络抖动的原因
网络抖动的主要原因之一是网络拥塞。当网络中的数据流量超过其处理能力时,数据包在传输过程中需要在网络设备中排队等待处理。这种排队延迟会导致端到端的延迟增加,并且由于每个数据包的排队时间可能不同,因此通过同一连接传输的分组延迟也会各不相同。
此外,当网络设备无法处理相同数据的流量时,其数据包缓冲区可能会满,并开始丢弃数据包。如果网络中没有干扰,每个数据包都会顺利到达。然而,如果端点缓冲区满了,数据包可能会受到延迟,导致到达的时间越来越晚,从而产生抖动。
解决方法
为了缓解网络抖动对实时性传输的影响,如VOIP和视频等,可以采用数据包接收端的抖动缓存技术。具体做法如下:
使用缓存指针队列:接收端通过缓存指针队列对接收到的数据包进行排序。每个数据包根据其到达的时间或序列号被插入到抖动缓存指针队列的相应位置。
定时器触发出队:抖动缓存指针队列配备一个出队线程定时器,该定时器以一定的时间间隔触发。当定时器触发时,出队线程会检查抖动缓存指针队列队头的数据包是否应该在当前时刻出队。如果是,则将该数据包从队列中移除并传递给上层应用进行处理。
通过这种方式,接收端能够有效地平滑网络抖动带来的延迟变化,提高实时性传输的质量。
HTTP和HTTPS区别
HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。
https则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。并且https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
同步问题
你传给服务器的是起始点和结束点吗?是否考虑过网络延迟导致客户端与服务端状态不一致的问题?
在项目中,我传给服务器的是起始点和方向(而不是结束点),服务器根据起始点和方向进行射线检测来计算命中结果。这样可以减少数据传输量,同时也能避免客户端和服务端状态不一致的问题。
关于网络延迟导致的状态不一致问题,我确实考虑到了。具体解决方案包括:
- 客户端预测:客户端在发送请求后立即模拟结果,减少玩家感知的延迟。
- 服务器校正:服务器定期发送校正数据,确保客户端状态与服务器一致。
针对网络延迟问题,你有什么解决方案?
针对网络延迟问题,我采用了以下解决方案:
(1) 客户端预测
- 客户端在发送请求后立即模拟结果,比如角色移动、开火等。
- 如果服务器返回的结果与客户端预测不一致,客户端会根据服务器数据进行校正。
(2) 插值和外推
- 插值:在客户端对远程玩家的位置进行插值,使移动更加平滑。
- 外推:根据远程玩家的速度和方向预测其下一个位置,减少延迟带来的卡顿感。
(3) 服务器权威
- 所有关键逻辑(如伤害计算、命中检测)都在服务器进行,确保游戏状态的准确性。
(4) 延迟补偿
- 服务器在计算命中时会考虑玩家的延迟,确保即使有延迟也能正确命中目标。
你提到的 RPC 底层原理是什么?(追问其实现机制)
RPC(Remote Procedure Call)的底层原理是通过网络调用远程服务,使其像调用本地函数一样简单。以下是 RPC 的实现机制:
(1) 客户端 Stub
- 客户端调用本地代理方法(Stub),Stub 将方法名和参数序列化为字节流。
(2) 网络传输
- 序列化后的数据通过网络发送到服务端。
(3) 服务端 Skeleton
- 服务端接收到数据后,反序列化为方法名和参数,并调用实际的业务逻辑方法。
(4) 结果返回
- 服务端将结果序列化并返回给客户端。
(5) 客户端接收
- 客户端接收到结果后,反序列化并返回给调用者。
【Unity面试篇】Unity 面试题总结甄选 |网络相关 | ❤️持续更新❤️_unity 网络 面试-CSDN博客
Unity游戏开发客户端面经——网络(初级)_游戏客户端开发 面试-CSDN博客