赞
踩
unity-多线程异步下载HttpWebRequest
使用的是 .net 里面的网络库 HttpWebRequest, 应用场景是需要同时下载多个小文件时, 效果很明显, 例如: 散文件热更.
效果如下, 最终下载完大小是 31,715KB
csharp 代码, 里面包含 unity 和 tolua 相关, 自行去掉即可
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using LuaInterface; using UnityEngine; public enum EMDErr : int { CreateRequest = -10001, GetResponse = -10002, NullResponse = -10003, ReadStream = -10004, } public class MultiDownObj { public string url; public string path; public LuaFunction luaFn; // 透传参数 [NoToLua] public bool isDone = false; [NoToLua] public int code; [NoToLua] public HttpWebRequest httpReq; [NoToLua] public HttpWebResponse httpRsp; [NoToLua] public byte[] buffer; [NoToLua] public Stream rspStream; [NoToLua] public FileStream fs; } // 多线程异步下载 public class MultiDownMgr : MonoBehaviour { private static MultiDownMgr _instance; public static MultiDownMgr Instance { get { return _instance; } } private const int BufferSize = 1024; private AsyncCallback rspCb = null; private AsyncCallback readCb = null; private List<MultiDownObj> objList = new List<MultiDownObj>(); public int timeout = 10000; // 10s 超时 public bool keepAlive = true; public int connectLimit { set { ServicePointManager.DefaultConnectionLimit = value; } // 并发线程数量 } void Awake() { _instance = this; rspCb = new AsyncCallback(ResponseCb); readCb = new AsyncCallback(ReadDataCb); connectLimit = 32; // 默认 32 个并发 } // lua 接口 public void Request(string url, string path, LuaFunction fn) { // LogUtil.D("--- Request, url: {0}, path: {1}", url, path); MultiDownObj mdObj = new MultiDownObj(); mdObj.url = url; mdObj.path = path; mdObj.luaFn = fn; RequestObj(mdObj); } void RequestObj(MultiDownObj mdObj) { objList.Add(mdObj); try { mdObj.httpReq = WebRequest.Create(mdObj.url) as HttpWebRequest; mdObj.httpReq.Method = "GET"; mdObj.httpReq.Timeout = timeout; mdObj.httpReq.KeepAlive = keepAlive; // 设置为 false 会导致中断下载, 报错: Remote prematurely closed connection. mdObj.httpReq.BeginGetResponse(rspCb, mdObj); } catch (System.Exception ex) { Close(mdObj); mdObj.code = (int) EMDErr.CreateRequest; mdObj.path = ex.Message; mdObj.isDone = true; } } void ResponseCb(IAsyncResult ar) { MultiDownObj mdObj = ar.AsyncState as MultiDownObj; try { HttpWebResponse response = mdObj.httpReq.EndGetResponse(ar) as HttpWebResponse; if (response == null) { Close(mdObj); mdObj.code = (int) EMDErr.NullResponse; mdObj.isDone = true; return; } mdObj.httpRsp = response; mdObj.code = (int) response.StatusCode; mdObj.rspStream = response.GetResponseStream(); if (response.StatusCode != HttpStatusCode.OK) { Close(mdObj); mdObj.isDone = true; return; } // 创建父目录 string dirPath = System.IO.Path.GetDirectoryName(mdObj.path); if (!Utils.IsDirectoryExist(dirPath)) { Utils.CreateDirectory(dirPath); } mdObj.fs = new FileStream(mdObj.path, FileMode.Create); mdObj.buffer = new byte[BufferSize]; mdObj.rspStream.BeginRead(mdObj.buffer, 0, BufferSize, readCb, mdObj); } catch (System.Exception ex) { Close(mdObj); mdObj.code = (int) EMDErr.GetResponse; mdObj.path = ex.Message; mdObj.isDone = true; } } void ReadDataCb(IAsyncResult ar) { MultiDownObj mdObj = ar.AsyncState as MultiDownObj; try { int read = mdObj.rspStream.EndRead(ar); if (read > 0) { mdObj.fs.Write(mdObj.buffer, 0, read); mdObj.fs.Flush(); mdObj.rspStream.BeginRead(mdObj.buffer, 0, BufferSize, readCb, mdObj); } else { Close(mdObj); mdObj.isDone = true; } } catch (System.Exception ex) { Close(mdObj); mdObj.code = (int) EMDErr.ReadStream; mdObj.path = ex.Message; mdObj.isDone = true; return; } } // 释放资源 void Close(MultiDownObj mdObj) { if (mdObj == null) return; if (mdObj.fs != null) { mdObj.fs.Close(); mdObj.fs = null; } if (mdObj.rspStream != null) { mdObj.rspStream.Close(); mdObj.rspStream = null; } if (mdObj.httpRsp != null) { mdObj.httpRsp.Close(); mdObj.httpRsp = null; } if (mdObj.httpReq != null) { mdObj.httpReq.Abort(); mdObj.httpReq = null; } mdObj.buffer = null; } public void StopDown(string url) { for (int i = 0; i < objList.Count; ++i) { if (objList[i].url == url) { objList.RemoveAt(i); i -= 1; } } } public void StopAll() { objList.Clear(); } private void Update() { for (int i = 0; i < objList.Count; ++i) { MultiDownObj mdObj = objList[i]; if (mdObj.isDone) { // LogUtil.D("--- mdObj done, code: {0}, path: {1}", mdObj.code, mdObj.path); if (mdObj.luaFn != null) { mdObj.luaFn.Call(mdObj.code, mdObj.url, mdObj.path); mdObj.luaFn.Dispose(); mdObj.luaFn = null; } objList.RemoveAt(i); i -= 1; } } } }
lua 测试代码
function gDebugCustom.MultiDown()
local function downFn(code, url, path)
gLog("--- code: {0}, url: {1}, path: {2}", code, url, path)
end
for i=1,5 do
local url = "https://www.aaa.com/download/game.apk"
local path = gTool.PathJoin(Application.persistentDataPath, string.formatExt("apks/aaa-{0}.apk", i))
MultiDownMgr.Instance:Request(url, path, downFn);
end
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。