U3D客户端框架之实现基于UnityWebRequest的Http服务 实现HttpCallBackArgs参数类、HttpRoutine访问器、HttpManager管理器

本文主要是介绍U3D客户端框架之实现基于UnityWebRequest的Http服务 实现HttpCallBackArgs参数类、HttpRoutine访问器、HttpManager管理器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 Http服务 访问器设计思路

        Unity3D 在2018版本中弃用了WWW请求,使用UnityWebRequest 进行网络请求,这个方法是为了满足今天的 HTTP 通信的需求,而且诞生的新类,相对于WWW这个方法,会更灵活一些,但是用起来却很不方便。

        所以我将UnityWebRequest封装了一下。封装的目的有两个:1.封装后访问Http用着方便;2.在框架层上隔离原生API和具体业务,即使后续API变更也不会影响到业务逻辑,避免业务受影响。

2 代码实现

Get和Post的区别可以看 引用模块中 Get和Post对比的链接,那篇文章中详细讲解了Get和Post的异同和使用场景。

HttpCallBackArgs:Http请求的回调数据,包装了是否有错、返回值、数据Bytes数组;说明一下HttpCallBackArgs继承EventArgs,是为了准守规范,让看代码的人一看到这个类型,就知道这是一个事件类型,使用的时候直接吧EventArgs转换成具体的事件参数类即可。

HttpCallBackArgs.cs 代码实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Myh
{//http请求的回调数据public class HttpCallBackArgs : EventArgs{//是否有错(是否发生了错误)public bool HasError;//返回值 public string Value;//字节数据 public byte[] Data;}
}

HttpRoutine:Http访问Url的轮询器,核心代码的所在文件;内部实现了GetUrl、PostUrl,状态监测、回调处理、对失败情况下的重试逻辑

HttpRoutine.cs 代码实现

using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using YouYou;namespace Myh
{//Http发送数据的回调委托 public delegate void HttpSendDataCallBack(HttpCallBackArgs args);//Http访问器public class HttpRoutine{//Http请求回调private HttpSendDataCallBack m_CallBack;//Http请求回调数据private HttpCallBackArgs m_CallBackArgs;//是否繁忙public bool IsBusy{get;private set;}//当前重试次数(尝试重新访问次数?)public int m_CurrRetry = 0;//URLprivate string m_Url;//是否是Getprivate bool m_IsGetData = false;//是否是Postprivate bool m_IsPost = false;//发送的数据private Dictionary<string, object> m_Dic;public HttpRoutine(){m_CallBackArgs = new HttpCallBackArgs();}#region SendData 发送Web数据public void SendData(string url, HttpSendDataCallBack cb, bool isPost = false,bool isGetData = false, Dictionary<string, object> dic = null){if (IsBusy)return;m_Url = url;m_CallBack = cb;m_IsPost = isPost;m_IsGetData = isGetData;m_Dic = dic;SendData();}private void SendData(){//不是post模式if (!m_IsPost){GetUrl(m_Url);}else{//把数据存到字典里if (m_Dic != null){//设备唯一Idm_Dic["deviceIdentifier"] = DeviceUtil.DeviceIdentifier;//设备型号m_Dic["deviceModel"] = DeviceUtil.DeviceModel;//服务器时间 TODO://还没有和服务器同步时间,暂时先用本地时间long t = DateTime.Now.Ticks;//用当前服务器时间 和 设备id 算一个md5出来 作为本地请求的签名(签名具有时效性,超时无效)string md5 = string.Format("{0}:{1}",t,DeviceUtil.DeviceIdentifier);m_Dic["sign"] = EncryptUtil.Md5(md5);//时间戳m_Dic["t"] = t;}string json = string.Empty;if (m_Dic != null){json = JsonMapper.ToJson(m_Dic);//不是get的方式if (!m_IsGetData){
#if DEBUG_LOG_PROTO && DEBUG_MODELGameEntry.Log(LogCategory.Proto, "<color=#ffa200>发送消息:</color><color=#FFFB80>" + m_Url + "</color>");GameEntry.Log(LogCategory.Proto, "<color=#ffdeb3>==>>" + json + "</color>");
#endif }GameEntry.Pool.EnqueueClassObject(m_Dic);}PostUrl(m_Url,json);}}#endregion#region GetUrl Get请求//Get请求private void GetUrl(string url){UnityWebRequest request = UnityWebRequest.Get(url);YouYou.GameEntry.Instance.StartCoroutine(Request(request));}#endregion#region PostUrl Post请求//Post请求private void PostUrl(string url, string json){//定义一个表单WWWForm form = new WWWForm();//给表单添加值form.AddField("json",json);//把url 和表单传入进去UnityWebRequest request = UnityWebRequest.Post(url,form);GameEntry.Instance.StartCoroutine(Request(request));}#endregion#region Request 请求服务器/** 功能:请求Web服务器* request:UnityWebRequest请求的实体*/private IEnumerator Request(UnityWebRequest request){//阻塞方法  和目标服务器建立连接,返回结果后才继续下一步yield return request.SendWebRequest();//如果有错误,重试与目标服务器连接(通信)if (request.isNetworkError || request.isHttpError){//报错了进行重试if (m_CurrRetry > 0){//过一段时间后再重新判断yield return new WaitForSeconds(GameEntry.Http.RetryInterval);}//重试次数+1++m_CurrRetry;//如果<=配置的重试次数if (m_CurrRetry <= GameEntry.Http.Retry){
#if DEBUG_LOG_PROTO && DEBUG_MODEL//通过宏开关,决定要不要打印logGameEntry.Log(LogCategory.Proto, "<color=#00eaff>请求URL:</color> <color=#00ff9c>{0}失败 当前重试次数{1}</color>", m_Url, m_CurrRetry);
#endif//调用SendData,重新发送数据SendData();//结束本次携程yield break;}//超过次数了,状态设置成有错误IsBusy = false;if (null != m_CallBack){m_CallBackArgs.HasError = true;m_CallBackArgs.Value = request.error;//不是GetData 方式的话 打印一个logif (!m_IsGetData){
#if DEBUG_LOG_PROTO && DEBUG_MODELGameEntry.Log(LogCategory.Proto, "<color=#00eaff>接收消息:</color> <color=#00ff9c>" + request.url + "</color>");GameEntry.Log(LogCategory.Proto, "<color=#c5e1dc>==>>" + JsonUtility.ToJson(m_CallBackArgs) + "</color>");
#endif}m_CallBack(m_CallBackArgs);}}//与主机建立连接else{IsBusy = false;if(null!=m_CallBack){m_CallBackArgs.HasError = false;m_CallBackArgs.Value = request.downloadHandler.text;if (!m_IsGetData){
#if DEBUG_LOG_PROTO && DEBUG_MODELGameEntry.Log(LogCategory.Proto, "<color=#00eaff>接收消息:</color> <color=#00ff9c>" + request.url + "</color>");GameEntry.Log(LogCategory.Proto, "<color=#c5e1dc>==>>" + JsonUtility.ToJson(m_CallBackArgs) + "</color>");
#endif}m_CallBackArgs.Data = request.downloadHandler.data;m_CallBack(m_CallBackArgs);}}//重试完毕,或者下载完毕m_CurrRetry = 0;m_Url = null;if (null != m_Dic){m_Dic.Clear();m_Dic = null;}m_CallBackArgs.Data = null;request.Dispose();request = null;//把Http访问器回池GameEntry.Pool.EnqueueClassObject(this);}#endregion}
}

HttpManager:Http服务器的管理类,记录了账号服务器的Url,通过HttpManager开启HttpRoutine。

HttpManager.cs 代码实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Myh
{public class HttpManager : ManagerBase, IDisposable{//正式服 账号服务器urlprivate string m_WebAccountUrl;//测试服 账号服务器urlprivate string m_TestWebAccountUrl;//是否是测试环境private bool m_IsTest;//真实账号服务器Urlpublic string RealWebAccountUrl{get{return m_IsTest ? m_TestWebAccountUrl : m_WebAccountUrl;}}//连接失败后重试次数public int Retry{get;private set;}//连接失败后重试间隔(单位:秒)public float RetryInterval{get;private set;}public override void Init(){//TODO:这些应该都要从设置里读取,可是现在没有,到时候回来改m_WebAccountUrl = "";m_TestWebAccountUrl = "";m_IsTest = true;Retry = 5;RetryInterval = 2f;}public void SendData(string url, HttpSendDataCallBack cb, bool isPost = false, bool isGetData = false, Dictionary<string, object> dic = null){//从类对象池里,获取http访问器HttpRoutine httpRoutine = YouYou.GameEntry.Pool.DequeueClassObject<HttpRoutine>();httpRoutine.SendData(url, cb, isPost, isGetData, dic);}public void Dispose(){}}
}

3 代码测试

Get访问网页会把网页内的Html代码读出来;Getzip会把zip下载下来。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Myh;
using YouYou;
using UnityEngine;public class TestHttp : ITest
{private void TestGetWebUrl(){GameEntry.Http.SendData("https://www.baidu.com", (HttpCallBackArgs args) =>{GameEntry.Log(LogCategory.Normal, "httpCallbackArgs hasError:{0} Value:{1} data str:{2}",args.HasError, args.Value, Encoding.UTF8.GetString(args.Data));});}private void TestGetDownloadUrl(){//从web站点上下载eee.zip文件GameEntry.Http.SendData("https://www.xxx.com/s/eee.zip", (HttpCallBackArgs args) =>{GameEntry.Log(LogCategory.Normal, "httpCallbackArgs hasError:{0} Value:{1} data str:{2}",args.HasError, args.Value, Encoding.UTF8.GetString(args.Data));});}public void OnTestStart(){}public void OnTestUpdate(){if (Input.GetKeyDown(KeyCode.Q)){TestGetWebUrl();}else if (Input.GetKeyDown(KeyCode.E)){TestGetDownloadUrl();}}
}


4 引用

Get和Post对比:HTTP请求中Get和Post的区别是什么?_天才小熊猫oo的博客-CSDN博客

这篇关于U3D客户端框架之实现基于UnityWebRequest的Http服务 实现HttpCallBackArgs参数类、HttpRoutine访问器、HttpManager管理器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/452881

相关文章

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque