参考:
deepseek、豆包
1 连接方式
TCP:面向连接,通信前三次握手建立连接,结束后四次挥手释放连接。
UDP:无连接,直接发送数据,无需预先建立连接。
2 可靠性
TCP:提供可靠传输,通过确认应答、超时重传、流量控制、拥塞控制等机制确保数据不丢失、不重复、按序到达。
UDP:不可靠传输,不保证数据到达顺序或是否丢失,无重传机制。
3 速度效率
TCP:传输速度慢,头部大(至少20字节),延迟较高。
UDP:无额外控制机制,头部小(仅8字节),传输更快,实时性更好。
4 流量控制
TCP:动态调整发送速率(滑动窗口、慢启动、拥塞避免等),避免网络过载。
UDP:无流量控制,可能发送过快导致丢包、网络拥塞。
5 应用场景:
TCP:网页浏览(HTTP)、文件传输(FTP)、电子邮件(SMTP)
UDP:视频会议、在线游戏、语音通话、直播流媒体
总结:
TCP可靠,但是效率低。
UDP不可靠,但是效率高。
websocket是基于TCP的,选择TCP的理由是websocket用于双向通信,例如聊天、游戏等,需要确保消息不丢失、按序到达。
而UDP不保证消息完整性,在视频中丢帧可以,但是聊天消息丢失是不可接受的。
三次握手
第一次握手:客户端告诉服务端我要连接你
第二次握手:服务端告诉客户端我收到了你的请求
第三次握手:客户端告诉服务端我收到了你的回复
通过三次握手,确认双方收发能力正常。
再仔细一看,确实通过三次握手,客户端收和发了一次,服务端也收和发了一次。

四次挥手
第一次挥手:客户端告诉服务端我不发数据了。
第二次挥手:服务端告诉客户端我收到了你不发数据的消息。
第三次挥手:服务端告诉客户端我也不发送数据了。
第四次挥手:客户端告诉服务端我收到了你不发数据的消息。
通过四次挥手,双方确认数据传输完毕,可以关闭连接。
为什么第二和第三次挥手不能合并?
因为服务端发送第二次挥手后还可能继续发送数据,如何合并了服务端就会立即关闭,未发送的数据会丢失。
那么为什么服务端不发送完数据后,再合并发送第二次和第三次挥手?
理论上服务端没有数据要发了,是可以合并第二次和第三次挥手,但是大多数操作系统默认不会合并,是严格执行四次挥手。

websocket是基于TCP的,无法直接使用UDP。
web可以用websocket模拟UDP,实际还是TCP。原生可以使用真正UDP,底层C++/Java实现通信,然后通过JS接口调用。
帧同步
1. 所有客户端上传操作指令。
2. 服务端收到所有客户端指令,然后广播给所有客户端。
3. 每个客户端收到广播的操作指令,计算游戏结果。

客户端如何保证同步的?
每个客户端在相同逻辑帧 + 执行相同的操作指令 = 相同的计算结果。
例如第一个逻辑帧A移动,客户端ABC都执行移动指令,那么客户端ABC上都会得到A移动的计算结果,A移动了10像素。
第二个逻辑帧C放技能,客户端ABC都执行放技能指令,那么客户端ABC上都会得到C放技能的计算结果,C放了一个火球术。
第三个逻辑帧B自杀了,客户端ABC都执行B自杀指令,那么客户端ABC上都会得到B自杀的计算结果,B死亡角色离开了游戏。
第N个逻辑帧,客户端ABC执行相同N个指令,都会得到相同的计算结果。
状态同步
1. 所有客户端上传操作指令。
2. 服务端计算结果,生成权威状态(玩家坐标、血量),并将结果广播给所有客户端。
3. 客户端根据服务端状态更新本地表现。

状态同步是服务端计算逻辑,所以计算结果是唯一的。
帧同步和状态同步区别
帧同步 状态同步
同步对象 操作指令(移动、技能) 游戏状态(玩家位置、血量、技能)
传输内容 较小 较大
逻辑计算 客户端计算逻辑 服务端计算逻辑
典型游戏 王者荣耀 魔兽世界、CS
帧同步是客户端计算逻辑,那么玩家可以修改本地数据,怎么防止作弊呢?
因为逻辑在客户端,所以没法彻底杜绝作弊,例如风灵月影,黑神话、鬼谷八荒啥都能改。

防止作弊的一些方法
1 操作指令校验:服务端进行操作校验,例如移动速度是否超出上限、技能释放是否符合冷却时间等。
2 关键帧校验:所有客户端上传游戏状态,哪个客户端数据和其它客户端不一致,就判定为作弊。
3 延迟追惩:服务端保存所有操作指令,然后模拟实现游戏过程,再对比客户端上传的结果。
4 客户端混淆:混淆代码,增加逆向难度。
5 内存加密:对关键变量保存时进行加密,例如攻击力进行异或后再保存。
deepseek给的一些案例,服务端校验移动合法性:
// 服务端:校验移动合法性
bool ValidateMovement(int playerId, Vector2 newPos, int currentFrame) {
// 获取上一帧位置
Vector2 lastPos = GetPlayerPosition(playerId, currentFrame - 1);
// 计算最大允许移动距离(速度*帧间隔)
float maxDistance = playerSpeed * frameInterval;
// 实际移动距离是否合法
if (Vector2.Distance(lastPos, newPos) > maxDistance * 1.2f) { // 允许20%误差
LogCheat(playerId, "Speed Hack");
return false;
}
return true;
}
预测和回滚
客户端为了玩家体验,减少延迟感,会先行进行预测,提前模拟执行操作指令,例如移动,射击。
但是预测和服务端不一致时,服务端会推翻客户端的计算结果,客户端需要修正状态。
例如玩CS延迟时,虽然你一直按着前跑,但是你的角色会不停被拉回原地。
那么如何优雅的回滚状态?
1 移动不一致:插值移动,从错误位置平滑移动到正确位置。但是实际上玩英雄联盟、球球大作战之类,太卡的时候,仍会有移动忽快忽慢,瞬移。
2 攻击判断不一致:客户端命中,服务端未命中。客户端先行表现射击,命中判定等待服务端返回。例如CS里你对着某人开枪怎么都打不死,就是服务端判定未命中。
3 回滚网络补偿:回滚到正确帧,然后再继续
4 预测容错:客户端允许存在小误差,例如位置偏移小于20像素 不纠正。
不同类型游戏的处理侧重

我觉得这个回滚很麻烦,所以还是客户端可以先行表现一些动作,例如射击、施法前摇等,具体的计算逻辑还是等服务端返回再说吧。
设计方案
竞技游戏:服务端权威,公平性>流畅性
休闲游戏:客户端预测,流畅性>公平性
说白了就是严谨点的游戏服务器跑逻辑,防止作弊,卡的话自己掏钱装好点宽带。
休闲点的游戏客户端跑逻辑,优先保证玩家流程体验,同时又减少了开发成本。
浙公网安备 33010602011771号