内容简介:Unity项目开发笔记(十六)目前产品在Unity端的开发任务已经趋于平稳 , 但是依旧达不到”非常稳定”的状态 , 随着用户量的增加 , 在复杂的PC网络和设备环境下 每次迭代版本都会频频中坑,这不 这次中招的是WWW和UnityWebRequest.我们知道现在的Unity (Mono2.0 + .NET 3.5) 在不增加第三方插件的情况下 有三种HTTP请求方式:
Unity项目开发笔记(十六)
目前产品在Unity端的开发任务已经趋于平稳 , 但是依旧达不到”非常稳定”的状态 , 随着用户量的增加 , 在复杂的PC网络和设备环境下 每次迭代版本都会频频中坑,这不 这次中招的是WWW和UnityWebRequest.
0x00. 基础介绍
我们知道现在的Unity (Mono2.0 + .NET 3.5) 在不增加第三方插件的情况下 有三种HTTP请求方式:
- WWW – 大家都知道,对于PC平台来说 底层封装了一遍Curl
- UnityWebRequest – Unity5.0以后新出来的一种方式
- HttpWebRequest – .net 自带的一种网络请求方式
以上三种请求方式各有优劣:
WWW主要是封装得比较便捷 , 下载AssetBoundle Texture等比较方便 省了转换和加载的过程.
UnityWebRequest主要是更加接近于 HttpWebRequest ,但是参数设置又比 HttpWebRequest 省事一点,适用于项目的大批量API实现 , 使用UnityWebRequest 可以比较自由的添加各种网络数据传输方式和格式.
HttpWebRequest.net框架自带 ,属于三种方式中自由度最大的 , 但是缺点也很明显 各种参数设置必须了然于胸 .
0x01. 问题重现
看下面一段代码
// 创建请求
WWW load = new WWW(url);
float timing = 0;
// 通过 isDone 判断是否结束
while (false == load.isDone)
{
// 设计超时
timing = timing + Time.deltaTime;
if (timing > m_fTimeOut)
{
if (onDownloaded != null)
{
onDownloaded(null);
}
load.Dispose();
yield break;
}
yield return null;
}
if (true == string.IsNullOrEmpty(load.error))
{
// 请求出错
}
else
{
// 请求成功
}
大多数情况下 以上代码是没有问题的,当然在Unity比较老的版本 (4.3以前) 在编辑器模式下是不能通过 isDone 来判断是否结束的. 在极少的情况下 以上参数 isDone 永远都为false , 目前这个BUG触发的概率大概为:
概率 = 1/50 x 1 / 500: 也就是 500个用户中会出现1个人 他在他的电脑中使用WWW发送50个请求其中有1个请求的isDone永远为false
通过对出现此问题的用户进行分析 ,发现大多数都集中在 联通和移动网络. 地区分布在云南贵州等等.
0x02. 解决方案(虽然一般都把解决方案放在文末 但是写这篇文章不为分享解决方案)
这个问题的解决方案不值一提 , 直接当使用 WWW 或者 UnityWebRequest 请求超时的时候使用 HttpWebRequest 进行当前请求即可. 对的 就是那么简单,伪代码为:
if(WWW is TimeOut)
{
GoTo HttpWebRequest;
}
else
{
Goto Finish;
}
深入WWW底层分析
虽然很轻易解决了问题 , 由于Unity的底层代码不开源 , 我们只能 绕道取荆州 , 但是遇到问题绕道一直都不是我的性格 , 现在让我们扒一扒这个WWW底层到底是什么 , 基于某种机缘巧合我手上获得一份Unity老版本的底层C代码 , 同时在这两年Unity官方开源了C#部分代码: 传送门 , 有了这两份东西我们就可以深入WWW看看底层究竟长什么样:
自顶向下 WWW 是这样调用的:
(第一步第二步属于公开代码内容 , 这里以文字呈现 , 后面的深入步骤属于闭源部分 这里只能以图片的方式.)
// 第一步 上层创建
WWW load = new WWW(strURL);
// 第二步 来到UnityEngine.dll
public WWW(string url)
{
this.InitWWW(url, null, null);
}
[MethodImpl(MethodImplOptions.InternalCall)]
public extern void InitWWW(string url, byte[] postData, string[] iHeaders);
第三步 来到C++底层 – 中间接口
第四步 来到C++底层 – 真正的WWW
这里主要注意 我们不是WebPlayer模式 . 也就是说会走到 CreatePlatformWWWBackend 中
第四步 来到C++底层 – CreatePlatformWWWBackend
一种跟踪 显然会走到 WWWCurl 里 , 注意 Curl,应该是指libcurl – 详解传送门 , 继续跟踪来到如下代码位置:
WWWCurl::WWWCurl -> DoInit -> StartThread -> WWW_ThreadEntryPoint -> GetURL
注意函数 WWW_ThreadEntryPoint 在 www.abortDownload 和 www.GetError() == NULL 都成立的情况下 才 www.FeedUnityWebStream(true) , 在curl整个调用过程中 都是 www.FeedUnityWebStream(false) , 说明isDone就在这个地方被改变.
最后我们看看这个GetURL是什么幺儿~
显然就是HTTP请求头的构造函数了.里面看到很多熟悉的字眼 , 使用fiddler随便抓一个Unity的请求就可以看到上面函数的数据头信息.
WWW底层代码的跟踪到此结束 . 基本上可以断定WWW之所以会发生isDone永远为false 和 curl有很大的关系 .下面我们看看isDone究竟是什么东西:
由上图 WWW 封装了一个 m_UnityWebStream 在 m_UnityWebStream 中保存 isDone数据. 看 IsFinished 函数即可确认:
而 IsFinished 函数里的数据 是根据上面 curl 的 WWWCurl::GetURL 函数中各种回调 WriteCallback,ReadCallback,ProgressCallback,HeaderCallback 中调用 FeedUnityWebStream 去修改的.
根据以上分析 ,如果要得出 isDone为何没有被设置为true, 只需重点跟踪 curl的几个回调函数即可. 根据以上代码 , 接下来准备分析一下libcurl的http请求 , 然后把这个模块单独抽出来 准备写个 工具 测试一下看看.
今天先到这里.
-EOF-
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 曲线救国,解决spring-boot2.0.6中webflux无法获得请求IP的问题
- Windows 10 解决无法完整下载安装语言包(日语输入法无法下载使用)
- ruby-on-rails – 无法推送到github,ssh:无法解析主机名
- erlang节点无法连接问题?
- erlang节点无法连接问题?
- 如何编写无法维护的代码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Paradigms of Artificial Intelligence Programming
Peter Norvig / Morgan Kaufmann / 1991-10-01 / USD 77.95
Paradigms of AI Programming is the first text to teach advanced Common Lisp techniques in the context of building major AI systems. By reconstructing authentic, complex AI programs using state-of-the-......一起来看看 《Paradigms of Artificial Intelligence Programming》 这本书的介绍吧!