对于客户端应用程序,免不了和远程服务打交道。设计一个良好的『服务层』能帮我们规范和分离业务代码,提高生产效率。服务层最核心的模块一定是怎样发送请求,虽然Mono提供了很多C#网络请求类,诸如WebClientHttpWebRequest,但考虑到跨平台,这些类不一定适用。不过不用担心,Unity 5.x提供了新的与网络相关类UnityWebRequest用来替代原先的WWW,这是官方推荐的,也是最佳选择。

使用Token进行身份验证

首先我们必须要考虑的是,怎样和Web服务安全的通信。没错,肯定是身份验证(Authentication)。对于像WebClient这些类,它们会提供一个属性,比如Credentials,可以在此属性设置一些身份验证信息,比如用户名,密码,域。这是一个很『重』的解决方案,且不论是否能在Unity中实现,单从密码这个角度,很多游戏根本不需要密码。所以,我们需要一种『轻』量级的身份验证机制,这就是Token,中文翻译叫『令牌』。

Token有两个重要的特点:

  • 代表了唯一的身份验证令牌
  • 具有时效性

第一点我们肯定可以理解,唯一性是身份验证的的基础。那第二点怎么理解呢?其实,Token本质上是一串加密过后的字符串,如果没有时效性,万一被窃取之后,他人很容易进行伪造。所以,易变的Token一定比不变的安全,你需要一个算法来动态生成Token,我提供一个简单的算法:

md5(((day*10) + (month*100) + (last2DigitsofYear)*1000)+userId+deviceId)

同理,你需要在Web服务前加上一个过滤器,一样的算法来验证Token是否一致。

Request Pipeline

Pipeline是管道的意思,管道是相连的,代表了请求的流转。由于UnityWebRequest必须配合StartCoroutine,而StartCoroutine又属于View层的代码,这和分层(详见之前的文章)冲突,MVVM框架需要将业务逻辑从View解耦。一个比较好的解决方案是通过中介的HttpTool来解决,它是一个单例的MonoBehaviour,并且不会随着场景的加载被销毁。

public class HttpTool : Singleton<HttpTool>
{
// 无法在外界使用构造函数,确保Singleton
protected HttpTool() { }
}

不管是请求还是响应,本质上是一堆数据的集合,将这些数据封装成对象的形式会更加容易管理,我将请求相关的数据封装成HttpRequest对象:

public class HttpRequest
{
public string Url { get; set; }
public HttpMethod Method { get; set; }
public string Parameters { get; set; }
}

而将从Web服务返回的数据封装成HttpResponse对象:

public class HttpResponse
{
public bool IsSuccess { get; set; }
public string Error { get; set; }
public long StatusCode { get; set; }
public string Data { get; set; }
}

值得注意的是,对应Http请求,不论Get还是Post都会将参数组装成“field1=value1&field2=value2”格式,不同的是Get请求,参数会跟在Url后,而Post请求则在Request Body里。所以需要一个帮助类,反射要传递的对象属性,拼装返回字符串。

核心的请求交由UnityWebRequest实现,通过yield等待返回的结果:

using (var www = UnityWebRequest.Get(url + parameters))
{
yield return www.Send();
var response = new HttpResponse
{
IsSuccess = !www.isError, Error = www.error, StatusCode = www.responseCode, Data = www.downloadHandler.text
};
onComplete(response);
}

最后再对返回的Json字符串反序列化成对象,值得注意的是,在此我用了内置的JsonUtility类,它并不能直接反序列化一个Json数组 ,而是需要将它包装成一个对象 ,通过集合类型属性的形式间接被反序列化。

至此,一个完整的Request Pipeline 如下图所示:

使用策略模式增强RemoteRepository

由于JsonUtility的限制因素多,你可能使用其他第三方的库。又或者不反序列化Json,而是Xml。所以在RemoteRepository中不应该限制死反序列化的代码,更好的想法是通过『策略模式』,交由外部算法来实现。这样的好处是你根本不需要改动RemoteRepository里的代码,这也符合『开闭原则』。

所以,你需要在RemoteRepository定义一个序列化接口:

public ISerializer Serializer { get; set; }

然后,对返回的HttpResponse中的Json反序列化:

Serializer.Deserialize<R>(httpResponse.Data)

真正的对Json序列化器实现了ISerializer接口,以策略的形式存在:

public class SerializerJson:ISerializer
{
public static readonly SerializerJson Instance=new SerializerJson();
private SerializerJson()
{ }
public string Serialize<T>(T obj, bool readableOutput = false) where T : class, new()
{
throw new NotImplementedException();
} public T Deserialize<T>(string json) where T : class, new()
{
return JsonUtility.FromJson<T>(json);
}
}

策略模式在编程领域运用非常广,比如Java或者.NET框架里的集合排序,大量用到策略模式。由程序员指定的算法来最终实现排序。

小结

本文的核心思想就是如何在合理分层结果下构建一个好用的服务层。谈到了如何动态生成Token来实现身份验证,以及分层情况下的请求流程。对于2D并且以数据绑定为基础的游戏,我认为这是一个好的实践方案。因为不管是三层架构还是N层架构,通过分层的好处是更加清晰去实现业务逻辑。

源代码托管在Github上,点击此了解

Unity应用架构设计(11)——一个网络层的构建的更多相关文章

  1. Unity应用架构设计(10)——绕不开的协程和多线程(Part 2)

    在上一回合谈到,客户端应用程序的所有操作都在主线程上进行,所以一些比较耗时的操作可以在异步线程上去进行,充分利用CPU的性能来达到程序的最佳性能.对于Unity而言,又提供了另外一种『异步』的概念,就 ...

  2. Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 2)

    MVVM回顾 经过上一篇文章的介绍,相信你对MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在. View只关心怎样渲染,而ViewModel只 ...

  3. Unity应用架构设计(13)——日志组件的实施

    对于应用程序而言,日志是非常重要的功能,通过日志,我们可以跟踪应用程序的数据状态,记录Crash的日志可以帮助我们分析应用程序崩溃的原因,我们甚至可以通过日志来进行性能的监控.总之,日志的好处很多,特 ...

  4. Unity应用架构设计(9)——构建统一的 Repository

    谈到 『Repository』 仓储模式,第一映像就是封装了对数据的访问和持久化.Repository 模式的理念核心是定义了一个规范,即接口『Interface』,在这个规范里面定义了访问以及持久化 ...

  5. Unity应用架构设计(6)——设计动态数据集合ObservableList

    什么是 『动态数据集合』 ?简而言之,就是当集合添加.删除项目或者重置时,能提供一种通知机制,告诉UI动态更新界面.有经验的程序员脑海里迸出的第一个词就是 ObservableCollection.没 ...

  6. Unity应用架构设计(4)——设计可复用的SubView和SubViewModel(Part 1)

    『可复用』这个词相信大家都熟悉,通过『可复用』的组件,可以大大提高软件开发效率. 值得注意的事,当我们设计一个可复用的面向对象组件时,需要保证其独立性,也就是我们熟知的『高内聚,低耦合』原则. 组件化 ...

  7. Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 1)

    初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...

  8. Unity应用架构设计(10)————绕不开的协程和多线程(Part 1)

    在进入本章主题之前,我们必须要了解客户端应用程序都是单线程模型,即只有一个主线程(Main Thread),或者叫做UI线程,即所有的UI控件的创建和操作都是在主线程上完成的.而服务器端应用程序,也就 ...

  9. Unity应用架构设计(12)——AOP思想的实践

    想象一下,当程序所有的业务逻辑都完成的时候,你可能还来不及喘口气,紧张的测试即将来临.你的Boss告诉你,虽然程序没问题,但某些方法为什么执行这么慢,性能堪忧.领会了Boss的意图之后,漫长的排查问题 ...

随机推荐

  1. 基于Haproxy+Keepalived构建高可用负载均衡集群

    实验环境: 主机名 IP地址 VIP 192.168.200.254 Haproxy-1 192.168.200.101 Haproxy-2 192.168.200.102 Nginx1 192.16 ...

  2. [编织消息框架][消息服务]jmx

    JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序.设备.系统等植入管理功能的框架,使用的是RMI技术. 比较经典的应用jdk bin目录下 jcons ...

  3. Windows 和 Mac 系统下安装git 并上传,修改项目

    首先在MAC上怎么操作. 在gitHub创立一个账户,在创立一个项目,这就不用我说了对吧. 创建完之后是这样的: 接下来,我们打开https://brew.sh 这是下载homebrew的网站,hom ...

  4. 杜教筛 && bzoj3944 Sum

    Description Input 一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N,代表一组询问 Output 一共T行,每行两个用空格分隔的数ans1,ans ...

  5. [ext4]空间管理 - 查找块

     在文件系统中,当需要执行写操作时,肯定是需要查找需要写入的块.那么如何查找块哪? 在Ext4系统中,有两个函数是可能执行查找操作的:ext4_getblk().ext4_get_block(). ...

  6. mysql导入导出sql文件(包括数据库和数据表的操作)

    废话不多说直接开始. 在windows命令行下登录mysql,创建一个test_01数据库,创建一个user表,并插入一条数据,如下 一.导出数据库test_01 1.退出数据库,在命令行中输入 my ...

  7. 记录下一个C++初始化的方式(很少有人这么用,但是却是一个使代码更加简洁的方式)

    很多时候,在一个类创建的时候给它初始化,一般呢,99%的人都会这么用: //A.h Class CA { int a; char* p; int getValue(); }; //A.cpp CA:: ...

  8. javascript 表达式和运算符 (二)

    表达式是一种JS短语,可使JS解释器用来产生一个值. 一.表达式 表达式分类 1.原始表达式 常量.直接量 (3.14,"test"); 关键字 (null,this,true): ...

  9. C#基础知识-函数的定义和调用(五)

    函数也可以称为方法,可以很方便的把一些行为封装到函数里面,当调用这一函数时会把函数块里面的代码按照顺序执行,方法可以有多种形式,有无参数,有无返回值等. 1. 函数的定义   函数定义的基本格式: s ...

  10. 画地为Mask,随心所欲的高效遮罩组件[Unity]

    在上一篇博文"扔掉遮罩,更好的圆形Image组件"中,笔者改变Image的顶点数据,使得Image呈圆形显示,避免了Mask的使用,从而节省Drawcall消耗,提高渲染效率了.这 ...