联机游戏网络相关内容总结

作者:ProcessCA
本篇难度:★★★☆☆
(请注意,本文是一次技术总结,对网络不太熟悉的童鞋食用前请做好心理准备)
大渣好。
最近整理了一下网络游戏相关的技术,主要由三个部分构成:
1.网络传输协议:HTTP,UDP,TCP

2.同步策略:状态同步,帧同步,还有介于两者之间的同步策略

3.序列化工具:Protobuf,自制工具

正是以上技术构成了网络游戏开发的基础,水平有限,欢迎指正。
在宏观的网络世界中,不同地区的电脑依靠互联网设施与自带的网卡就可以进行网络通信,因为互联网本身就由一系列协议组成,在OSI(开放系统互联)模型的基础上,TCP/IP精简了不少东西,从7层模型变为4层模型,遵循这套协议的机器就可以实现网络通信。

网络传输协议
游戏开发中我们只需要关注TCP/IP协议族中的应用层与传输层。
应用层提供了许多协议支持不同应用的开发,比如通过浏览器访问知乎用到了HTTPS协议,HTTPS是在HTTP基础上加入了一层加密的协议。
在卡牌,跑酷这种弱交互游戏中,一般没有实时对战,偶尔请求服务器进行抽卡或者查看排行榜。可以借助应用层的HTTP协议进行通信,HTTP协议基于请求-相应模型,较为简单,方便调试。

举个HTTP协议在卡牌游戏中抽卡的例子:
1.客户端向服务器发送Get或Post请求。
2.服务器接受到消息后进行抽卡逻辑。
3.服务器向客户端发送请求的相应。
4.客户端接收到回应消息后进行显示操作。
然后就抽中了:

如果涉及到了实时交互的游戏,如RPG,MMO这种往往会使用传输层的TCP,UDP协议保证实时交互。具体选择什么协议得看具体的项目,对传输带宽与速度有严格要求推荐使用UDP,否则推荐使用TCP确保可靠性,降低开发成本。两种协议搭配实现游戏中的不同网络模块也是十分常见的解决方案。
以下是两种协议的对比:

同步策略
同步策略可分为状态同步,帧同步与介于两者之间的同步,关于状态同步与帧同步,网上有大量的文章介绍,大家也有不同的理解。下面说说我的看法:
状态同步:从字面意思上理解就是客户端通过服务器同步自己的状态。这个状态指的是具体的数值。比如向服务器发送:我的旋转角度改为(0, 90, 0),我的坐标更新为(100, 100, 0),这个过程服务器可以不进行逻辑判断。

但是基于这种纯粹的方式无法直接检测客户端发送数据的真实性,为了防止严重的作弊,可以对每次客户端发送的数据进行范围限制,确保结果在一个可接受范围内。
在利用UnityNetworking进行原型开发时用到的[SyncVar]特性,它的作用就是在网络中同步指定字段的值。不得不说利用这种策略去堆叠游戏的原型的时候开发效率非常高。

帧同步:指每个客户端只同步自己的操作。操作可以是:Q, W, E, R这种按键,也可以是鼠标滑动后产生的向量。操作上传给服务器后,服务器直接转发给其他的客户端,在每个客户端上进行逻辑运算,通过确保不同客户端在相同的时间接受输入,在相同时间得到相同的结果。
关于确保帧同步的准确性有两种常用的方式:LockStep(锁步)与乐观的帧同步。
为了确保比赛结果的准确性,帧同步服务器上可以运行一套客户端参与运算,结果以其为准。但是一个无法解决的问题是:逻辑运算变成了客户端的事,我们完全可以通过修改客户端本身实现作弊,比如开启全图视野,这种外挂服务器是无法预料的。
一般对于这种情况会依赖一些其他的工具验证客户端程序的完整性,内存监控等方式弥补这一点缺陷。但是无法根本上杜绝作弊,所以我们会在FPS,RTS这些应用帧同步比较广泛的游戏类型中看到各种全图挂,透视挂层出不穷。

但是对于格斗游戏,采取帧同步算是最正确的做法。

格斗游戏对延迟敏感,一局游戏的所有信息对双方的完全透明。所以基本上联机格斗游戏都会选择帧同步。
基于两者之间:现实的网络游戏中情况比较复杂,要防止客户端作弊,最简单有效的办法是把游戏的大部分逻辑放在服务器上面。这时就不是客户端说了算,客户端只能发送一些操作指令比如:我要向左转90度,跑向一个目标点。
服务器收后客户端的指令后进行逻辑运算,然后把结果同步给所有客户端。这样的好处显而易见,客户端没有办法直接修改数据,基本上防止了作弊。
但是缺点也明显:就是服务器压力大,流量消耗大。特别是针对大型网游,会采取一系列优化措施保障服务器的稳定。
比如魔兽世界主要采用这种方式进行同步:

序列化工具
在这里要说的一点是关于序列化工具首推的是Protobuf,因为其序列化效率高,易用,跨语言的特性使得protobuf在游戏开发中有着十分广泛的应用。如果有能力自己实现一套序列化工具也是可以的,用C#的API就可以做到:
using System.Net;using System.Net.Sockets;using System.IO;using System.Runtime.Serialization.Formatters.Binary;using System.Text;/// <summary>/// 网络工具类 <see langword="static"/>/// </summary>public static class NetworkUtils{
//序列化:obj -> byte[]
public static byte[] Serialize(object obj)
{
//对象必须被标记为Serializable
if (obj == null || !obj.GetType().IsSerializable)
return null;
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, obj);
byte[] data = stream.ToArray();
return data;
}
}
//反序列化:byte[] -> obj
public static T Deserialize<T>(byte[] data) where T : class
{
//T必须是可序列化的类型
if (data == null || !typeof(T).IsSerializable)
return null;
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream(data))
{
object obj = formatter.Deserialize(stream);
return obj as T;
}
}}
在之前的文章中有对Protobuf与C#序列化工具详细的介绍,这里放出连接:
OK,关于网络游戏的简单总结就到这里,希望本文对在游戏开发的道路上的你有所启发。
(顺便说一下,之后的文章还是会搭配具体的游戏。)
想系统学习游戏开发的童鞋,欢迎访问 http://levelpp.com/
游戏开发搅基QQ群:869551769
微信公众号:皮皮关