unity-多线程异步下载HttpWebRequest
Posted 蝶泳奈何桥.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity-多线程异步下载HttpWebRequest相关的知识,希望对你有一定的参考价值。
title: unity-多线程异步下载HttpWebRequest
categories: Unity3d
tags: [unity, 多线程, 异步, 下载]
date: 2022-07-02 14:52:38
comments: false
mathjax: true
toc: true
unity-多线程异步下载HttpWebRequest
前篇
- 官方 - https://docs.microsoft.com/zh-cn/dotnet/framework/network-programming/making-asynchronous-requests
使用的是 .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
以上是关于unity-多线程异步下载HttpWebRequest的主要内容,如果未能解决你的问题,请参考以下文章