内容简介: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节点无法连接问题?
- 如何编写无法维护的代码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming the Mobile Web
Maximiliano Firtman / O'Reilly Media / 2010-07-23 / $44.99
* Learn how to use your existing skills to move into mobile web development * Discover the new possibilities of mobile web development, and understand its limitations * Get detailed coverage of ......一起来看看 《Programming the Mobile Web》 这本书的介绍吧!