From 850d9f3cb04631634fe3012d1d613a8085aa0e4b Mon Sep 17 00:00:00 2001 From: sin365 <353374337@qq.com> Date: Tue, 24 Sep 2024 18:16:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OnlySocketHttp.cs | 922 ++++++++++++++++++++++++++++++++++++++++++ UnityWaitOSRequest.cs | 19 + 2 files changed, 941 insertions(+) create mode 100644 OnlySocketHttp.cs create mode 100644 UnityWaitOSRequest.cs diff --git a/OnlySocketHttp.cs b/OnlySocketHttp.cs new file mode 100644 index 0000000..c70f504 --- /dev/null +++ b/OnlySocketHttp.cs @@ -0,0 +1,922 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; + +namespace OnlySocketHttp +{ + public class RequestAsync + { + public OnlySocketHttpResponseInfo resp; + public bool isDone; + public bool bIsSuccess => resp != null ? resp.bTimeOut : false; + } + public class OnlySocketHttpResponseInfo + { + public string host = "";//host主机头 + public string url = "";//pathAndQuery + public int port = 80; + public string request = ""; + public string encoding = ""; + public string header = ""; + public string body = ""; + public string reuqestBody = ""; + public string reuqestHeader = ""; + public Dictionary headers = new Dictionary(); + public string response = ""; + public string gzip = ""; + public int length = 0; + public int code = 0; + public int location = 0; + public int runTime = 0;//获取网页消耗时间,毫秒 + public int sleepTime = 0;//休息时间 + public string cookies = ""; + public bool bTimeOut = false; + } + + public class OnlySocketHttp + { + public const char T = '\n'; + public const string CT = "\r\n"; + public const string CTRL = "\r\n\r\n"; + public const string Content_Length_Str = "content-length: "; + public const string Content_Length_Str_M = "Content-Length: "; + public const string Content_Length = "content-length"; + public const string Content_Encoding = "content-encoding"; + public const string Transfer_Encoding = "transfer-encoding"; + public const string Connection = "connection"; + public static long index = 0; + + + public static RequestAsync HttpRequestAsync(string url) + { + RequestAsync requestAsync = new RequestAsync(); + string strURI = url; + string strHost = ""; + string strIP = ""; + string strPort = ""; + string strRelativePath = ""; + bool bSSL = false; + bool foward_302 = true; + + if (!ParseURI(strURI, ref bSSL, ref strHost, ref strIP, ref strPort, ref strRelativePath)) + { + requestAsync.isDone = true; + requestAsync.resp = null; + //URL 不合法 也可以处理成404 + return requestAsync; + } + int port; + int.TryParse(strPort, out port); + + UnityEngine.Debug.Log($"strURI->{strURI}"); + UnityEngine.Debug.Log($"strHost->{strHost}"); + UnityEngine.Debug.Log($"strPort->{port}"); + UnityEngine.Debug.Log($"strRelativePath->{strRelativePath}"); + + Thread thread = new Thread( + () => + { + + // 构造HTTP请求报文 + string requestContent = $"GET {strRelativePath} HTTP/1.1\r\n" + + $"Accept-Language: en-us\r\n" + + $"Accept-Encoding: gzip,deflate\r\n" + + $"Host: {strHost}\r\n" + + "Connection: close\r\n" + + "\r\n"; + try + { + OnlySocketHttpResponseInfo resp = OnlySocketHttp.sendRequestRetry(bSSL, 1, strHost, port, "", + requestContent, + 5, + "UTF-8", + foward_302); + requestAsync.resp = resp; + requestAsync.isDone = true; + } + catch + { + requestAsync.resp = null; + requestAsync.isDone = true; + } + }); + thread.Start(); + return requestAsync; + } + + public static bool ParseURI(string strURI, ref bool bIsSSL, ref string strHost, ref string strIP, ref string strPort, ref string strRelativePath) + { + string strAddressRet; + string strPortRet; + string strRelativePathRet; + string strIPRet; + + string strProtocol = strURI.Substring(0, 7); + if (strProtocol != "http://") + return false; + + bIsSSL = strURI.ToLower().StartsWith("https://"); + + string strLeft = strURI.Substring(7, strURI.Length - 7); + int nIndexPort = strLeft.IndexOf(':'); + if (nIndexPort == -1) + { + strPortRet = "80"; + int nIndexRelative = strLeft.IndexOf('/'); + if (nIndexRelative != -1) + { + strAddressRet = strLeft.Substring(0, nIndexRelative); + strRelativePathRet = strLeft.Substring(nIndexRelative, strLeft.Length - nIndexRelative); + } + else + return false; + } + else + { + strAddressRet = strLeft.Substring(0, nIndexPort); + int nIndexRelative = strLeft.IndexOf('/'); + if (nIndexRelative != -1) + { + strPortRet = strLeft.Substring(nIndexPort + 1, nIndexRelative - (nIndexPort + 1)); + strRelativePathRet = strLeft.Substring(nIndexRelative, strLeft.Length - nIndexRelative); + } + else + return false; + } + strHost = strAddressRet; + try + { + IPHostEntry hostinfo = Dns.GetHostEntry(strAddressRet); + IPAddress[] aryIP = hostinfo.AddressList; + strIPRet = aryIP[0].ToString(); + } + catch + { + return false; + } + + strIP = strIPRet; + strPort = strPortRet; + strRelativePath = UrlEncode(strRelativePathRet); + return true; + } + + public static string UrlEncode(string str) + { + string sb = ""; + List filter = new List() { '!', '#', '$', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '=', '?', '@', '_', '~' }; + byte[] byStr = System.Text.Encoding.UTF8.GetBytes(str); //System.Text.Encoding.Default.GetBytes(str) + for (int i = 0; i < byStr.Length; i++) + { + if (filter.Contains((char)byStr[i])) + sb += (char)byStr[i]; + else if ((char)byStr[i] >= 'a' && (char)byStr[i] <= 'z') + sb += (char)byStr[i]; + else if ((char)byStr[i] >= 'A' && (char)byStr[i] <= 'Z') + sb += (char)byStr[i]; + else if ((char)byStr[i] >= '0' && (char)byStr[i] <= '9') + sb += (char)byStr[i]; + else + sb += (@"%" + Convert.ToString(byStr[i], 16)); + } + return sb; + } + + /** + * + 发生异常尝试重连 + * + */ + public static OnlySocketHttpResponseInfo sendRequestRetry(Boolean isSSL, int tryCount, String host, int port, String payload, String request, int timeout, String encoding, Boolean foward_302) + { + int count = 0; + Interlocked.Increment(ref index); + OnlySocketHttpResponseInfo server = new OnlySocketHttpResponseInfo(); + timeout = timeout * 1000; + while (true) + { + if (count >= tryCount) break; + + try + { + if (!isSSL) + { + server = sendHTTPRequest(count, host, port, payload, request, timeout, encoding, foward_302); + return server; + } + else + { + + server = sendHTTPSRequest(count, host, port, payload, request, timeout, encoding, foward_302); + return server; + + } + } + catch (Exception e) + { + UnityEngine.Debug.Log("发包发生异常,正在重试----" + e.Message); + server.bTimeOut = true; + continue; + } + finally + { + count++; + } + + } + return server; + + } + + private static void checkContentLength(ref OnlySocketHttpResponseInfo server, ref String request) + { + + //重新计算并设置Content-length + int sindex = request.IndexOf(CTRL); + server.reuqestHeader = request; + if (sindex != -1) + { + server.reuqestHeader = request.Substring(0, sindex); + server.reuqestBody = request.Substring(sindex + 4, request.Length - sindex - 4); + int contentLength = Encoding.UTF8.GetBytes(server.reuqestBody).Length; + String newContentLength = Content_Length_Str_M + contentLength; + + if (request.IndexOf(Content_Length_Str_M) != -1) + { + request = Regex.Replace(request, Content_Length_Str_M + "\\d+", newContentLength); + } + else + { + request = request.Insert(sindex, "\r\n" + newContentLength); + } + } + else + { + request = Regex.Replace(request, Content_Length_Str + "\\d+", Content_Length_Str_M + "0"); + request += CTRL; + } + + + } + + private static void doHeader(ref OnlySocketHttpResponseInfo server, ref String[] headers) + { + + for (int i = 0; i < headers.Length; i++) + { + if (i == 0) + { + + server.code = Tools.convertToInt(headers[i].Split(' ')[1]); + + } + else + { + String[] kv = Regex.Split(headers[i], ": "); + String key = kv[0].ToLower(); + if (!server.headers.ContainsKey(key)) + { + //自动识别编码 + if ("content-type".Equals(key)) + { + String hecnode = getHTMLEncoding(kv[1], ""); + if (!String.IsNullOrEmpty(hecnode)) + { + server.encoding = hecnode; + } + } + if (kv.Length > 1) + { + server.headers.Add(key, kv[1]); + } + else + { + server.headers.Add(key, ""); + } + } + } + } + + } + + + private static OnlySocketHttpResponseInfo sendHTTPRequest(int count, String host, int port, String payload, String request, int timeout, String encoding, Boolean foward_302) + { + + String index = Thread.CurrentThread.Name + OnlySocketHttp.index; + Stopwatch sw = new Stopwatch(); + sw.Start(); + OnlySocketHttpResponseInfo server = new OnlySocketHttpResponseInfo(); + TcpClient clientSocket = null; + int sum = 0; + try + { + if (port > 0 && port <= 65556) + { + //编码处理 + server.request = request; + TimeOutSocket tos = new TimeOutSocket(); + clientSocket = tos.Connect(host, port, timeout); + if (sw.ElapsedMilliseconds >= timeout) + { + return server; + } + clientSocket.SendTimeout = timeout - tos.useTime; + if (clientSocket.Connected) + { + checkContentLength(ref server, ref request); + server.request = request; + + byte[] requestByte = Encoding.UTF8.GetBytes(request); + clientSocket.Client.Send(requestByte); + byte[] responseBody = new byte[1024 * 1000]; + int len = 0; + //获取header头 + String tmp = ""; + StringBuilder sb = new StringBuilder(); + clientSocket.ReceiveTimeout = timeout - (int)sw.ElapsedMilliseconds; + do + { + byte[] responseHeader = new byte[1]; + len = clientSocket.Client.Receive(responseHeader, 1, SocketFlags.None); + if (len == 1) + { + + char c = (char)responseHeader[0]; + sb.Append(c); + if (c.Equals(T)) + { + tmp = String.Concat(sb[sb.Length - 4], sb[sb.Length - 3], sb[sb.Length - 2], c); + } + } + } while (!tmp.Equals(CTRL) && sw.ElapsedMilliseconds < timeout); + + server.header = sb.ToString().Replace(CTRL, ""); + String[] headers = Regex.Split(server.header, CT); + if (headers != null && headers.Length > 0) + { + //处理header + doHeader(ref server, ref headers); + //自动修正编码 + if (!String.IsNullOrEmpty(server.encoding)) + { + encoding = server.encoding; + } + Encoding encod = Encoding.GetEncoding(encoding); + + //302 301跳转 + if ((server.code == 302 || server.code == 301) && foward_302) + { + StringBuilder rsb = new StringBuilder(server.request); + int urlStart = server.request.IndexOf(" ") + 1; + int urlEnd = server.request.IndexOf(" HTTP"); + if (urlStart != -1 && urlEnd != -1) + { + String url = server.request.Substring(urlStart, urlEnd - urlStart); + rsb.Remove(urlStart, url.Length); + String location = server.headers["location"]; + if (!server.headers["location"].StartsWith("/") && !server.headers["location"].StartsWith("http")) + { + location = Tools.getCurrentPath(url) + location; + } + rsb.Insert(urlStart, location); + + return sendHTTPRequest(count, host, port, payload, rsb.ToString(), timeout, encoding, false); + } + + } + + + //根据请求头解析 + if (server.headers.ContainsKey(Content_Length)) + { + int length = int.Parse(server.headers[Content_Length]); + + while (sum < length && sw.ElapsedMilliseconds < timeout) + { + int readsize = length - sum; + len = clientSocket.Client.Receive(responseBody, sum, readsize, SocketFlags.None); + if (len > 0) + { + sum += len; + } + } + } + //解析chunked传输 + else if (server.headers.ContainsKey(Transfer_Encoding)) + { + //读取长度 + int chunkedSize = 0; + byte[] chunkedByte = new byte[1]; + //读取总长度 + sum = 0; + do + { + String ctmp = ""; + do + { + len = clientSocket.Client.Receive(chunkedByte, 1, SocketFlags.None); + ctmp += Encoding.UTF8.GetString(chunkedByte); + + } while ((ctmp.IndexOf(CT) == -1) && (sw.ElapsedMilliseconds < timeout)); + + chunkedSize = Tools.convertToIntBy16(ctmp.Replace(CT, "")); + + //chunked的结束0\r\n\r\n是结束标志,单个chunked块\r\n结束 + if (ctmp.Equals(CT)) + { + continue; + } + if (chunkedSize == 0) + { + //结束了 + break; + } + int onechunkLen = 0; + while (onechunkLen < chunkedSize && sw.ElapsedMilliseconds < timeout) + { + len = clientSocket.Client.Receive(responseBody, sum, chunkedSize - onechunkLen, SocketFlags.None); + if (len > 0) + { + onechunkLen += len; + sum += len; + } + } + + //判断 + } while (sw.ElapsedMilliseconds < timeout); + } + //connection close方式或未知body长度 + else + { + while (sw.ElapsedMilliseconds < timeout) + { + if (clientSocket.Client.Poll(timeout, SelectMode.SelectRead)) + { + if (clientSocket.Available > 0) + { + len = clientSocket.Client.Receive(responseBody, sum, (1024 * 200) - sum, SocketFlags.None); + if (len > 0) + { + sum += len; + } + } + else + { + break; + } + } + } + } + //判断是否gzip + if (server.headers.ContainsKey(Content_Encoding)) + { + server.body = unGzip(responseBody, sum, encod); + } + else + { + server.body = encod.GetString(responseBody, 0, sum); + } + + + } + } + + } + } + catch (Exception e) + { + Exception ee = new Exception("HTTP发包错误!错误消息:" + e.Message + e.TargetSite.Name + "----发包编号:" + index); + throw ee; + } + finally + { + sw.Stop(); + server.length = sum; + server.runTime = (int)sw.ElapsedMilliseconds; + if (clientSocket != null) + { + clientSocket.Close(); + } + } + return server; + + } + + private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + { + return true; + } + private static OnlySocketHttpResponseInfo sendHTTPSRequest(int count, String host, int port, String payload, String request, int timeout, String encoding, Boolean foward_302) + { + String index = Thread.CurrentThread.Name + OnlySocketHttp.index; + Stopwatch sw = new Stopwatch(); + sw.Start(); + OnlySocketHttpResponseInfo server = new OnlySocketHttpResponseInfo(); + + int sum = 0; + + TcpClient clientSocket = null; ; + + try + { + + if (port > 0 && port <= 65556) + { + + TimeOutSocket tos = new TimeOutSocket(); + clientSocket = tos.Connect(host, port, timeout); + if (sw.ElapsedMilliseconds >= timeout) + { + return server; + } + clientSocket.SendTimeout = timeout - tos.useTime; + + SslStream ssl = null; + if (clientSocket.Connected) + { + ssl = new SslStream(clientSocket.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate)); + SslProtocols protocol = SslProtocols.Ssl3 | SslProtocols.Ssl2 | SslProtocols.Tls; + ssl.AuthenticateAsClient(host, null, protocol, false); + if (ssl.IsAuthenticated) + { + checkContentLength(ref server, ref request); + server.request = request; + byte[] requestByte = Encoding.UTF8.GetBytes(request); + ssl.Write(requestByte); + ssl.Flush(); + } + } + server.request = request; + byte[] responseBody = new byte[1024 * 1000]; + int len = 0; + //获取header头 + String tmp = ""; + + StringBuilder sb = new StringBuilder(); + StringBuilder bulider = new StringBuilder(); + clientSocket.ReceiveTimeout = timeout - (int)sw.ElapsedMilliseconds; + do + { + byte[] responseHeader = new byte[1]; + int read = ssl.ReadByte(); + + char c = (char)read; + sb.Append(c); + if (c.Equals(T)) + { + tmp = String.Concat(sb[sb.Length - 4], sb[sb.Length - 3], sb[sb.Length - 2], c); + } + + } while (!tmp.Equals(CTRL) && sw.ElapsedMilliseconds < timeout); + + server.header = sb.ToString().Replace(CTRL, ""); + String[] headers = Regex.Split(server.header, CT); + //处理header + doHeader(ref server, ref headers); + //自动修正编码 + if (!String.IsNullOrEmpty(server.encoding)) + { + encoding = server.encoding; + } + Encoding encod = Encoding.GetEncoding(encoding); + //302 301跳转 + if ((server.code == 302 || server.code == 301) && foward_302) + { + + int urlStart = server.request.IndexOf(" "); + int urlEnd = server.request.IndexOf(" HTTP"); + if (urlStart != -1 && urlEnd != -1) + { + String url = server.request.Substring(urlStart + 1, urlEnd - urlStart - 1); + if (!server.headers["location"].StartsWith("/") && !server.headers["location"].StartsWith("https")) + { + server.request = server.request.Replace(url, Tools.getCurrentPath(url) + server.headers["location"]); + } + else + { + server.request = server.request.Replace(url, server.headers["location"]); + } + + return sendHTTPSRequest(count, host, port, payload, server.request, timeout, encoding, false); + } + + } + + + //根据请求头解析 + if (server.headers.ContainsKey(Content_Length)) + { + int length = int.Parse(server.headers[Content_Length]); + while (sum < length && sw.ElapsedMilliseconds < timeout) + { + len = ssl.Read(responseBody, sum, length - sum); + if (len > 0) + { + sum += len; + } + } + } + //解析chunked传输 + else if (server.headers.ContainsKey(Transfer_Encoding)) + { + //读取长度 + int chunkedSize = 0; + byte[] chunkedByte = new byte[1]; + //读取总长度 + sum = 0; + do + { + String ctmp = ""; + do + { + len = ssl.Read(chunkedByte, 0, 1); + ctmp += Encoding.UTF8.GetString(chunkedByte); + + } while (ctmp.IndexOf(CT) == -1 && sw.ElapsedMilliseconds < timeout); + + chunkedSize = Tools.convertToIntBy16(ctmp.Replace(CT, "")); + + //chunked的结束0\r\n\r\n是结束标志,单个chunked块\r\n结束 + if (ctmp.Equals(CT)) + { + continue; + } + if (chunkedSize == 0) + { + //结束了 + break; + } + int onechunkLen = 0; + + while (onechunkLen < chunkedSize && sw.ElapsedMilliseconds < timeout) + { + len = ssl.Read(responseBody, sum, chunkedSize - onechunkLen); + if (len > 0) + { + onechunkLen += len; + sum += len; + } + } + + //判断 + } while (sw.ElapsedMilliseconds < timeout); + } + //connection close方式或未知body长度 + else + { + while (sw.ElapsedMilliseconds < timeout) + { + if (clientSocket.Client.Poll(timeout, SelectMode.SelectRead)) + { + if (clientSocket.Available > 0) + { + len = ssl.Read(responseBody, sum, (1024 * 200) - sum); + if (len > 0) + { + sum += len; + } + } + else + { + break; + } + } + } + } + //判断是否gzip + if (server.headers.ContainsKey(Content_Encoding)) + { + server.body = unGzip(responseBody, sum, encod); + } + else + { + server.body = encod.GetString(responseBody, 0, sum); + } + } + + } + catch (Exception e) + { + Exception ee = new Exception("HTTPS发包错误!错误消息:" + e.Message + "----发包编号:" + index); + throw ee; + } + finally + { + sw.Stop(); + server.length = sum; + server.runTime = (int)sw.ElapsedMilliseconds; + + if (clientSocket != null) + { + clientSocket.Close(); + } + } + return server; + + } + + public static String unGzip(byte[] data, int len, Encoding encoding) + { + + String str = ""; + MemoryStream ms = new MemoryStream(data, 0, len); + GZipStream gs = new GZipStream(ms, CompressionMode.Decompress); + MemoryStream outbuf = new MemoryStream(); + byte[] block = new byte[1024]; + + try + { + + while (true) + { + int bytesRead = gs.Read(block, 0, block.Length); + if (bytesRead <= 0) + { + break; + } + else + { + outbuf.Write(block, 0, bytesRead); + } + } + str = encoding.GetString(outbuf.ToArray()); + } + catch (Exception e) + { + UnityEngine.Debug.Log("解压Gzip发生异常----" + e.Message); + } + finally + { + outbuf.Close(); + gs.Close(); + ms.Close(); + + } + return str; + + } + public static String getHTMLEncoding(String header, String body) + { + if (String.IsNullOrEmpty(header) && String.IsNullOrEmpty(body)) + { + return ""; + } + body = body.ToUpper(); + Match m = Regex.Match(header, @"charset\b\s*=\s*""?(?[^""]*)", RegexOptions.IgnoreCase); + if (m.Success) + { + return m.Groups["charset"].Value.ToUpper(); + } + else + { + if (String.IsNullOrEmpty(body)) + { + return ""; + } + m = Regex.Match(body, @"charset\b\s*=\s*""?(?[^""]*)", RegexOptions.IgnoreCase); + if (m.Success) + { + return m.Groups["charset"].Value.ToUpper(); + } + } + return ""; + } + } + + + class TimeOutSocket + { + private bool IsConnectionSuccessful = false; + private Exception socketexception = null; + private ManualResetEvent TimeoutObject = new ManualResetEvent(false); + public int useTime = 0; + public TcpClient Connect(String host, int port, int timeoutMSec) + { + Stopwatch sw = new Stopwatch(); + sw.Start(); + TimeoutObject.Reset(); + socketexception = null; + + TcpClient tcpclient = new TcpClient(); + + tcpclient.BeginConnect(host, port, new AsyncCallback(CallBackMethod), tcpclient); + + + if (TimeoutObject.WaitOne(timeoutMSec, false)) + { + if (IsConnectionSuccessful) + { + sw.Stop(); + useTime = (int)sw.ElapsedMilliseconds; + return tcpclient; + } + else + { + throw socketexception; + } + } + else + { + tcpclient.Close(); + throw new TimeoutException("TimeOut Exception"); + } + } + private void CallBackMethod(IAsyncResult asyncresult) + { + try + { + IsConnectionSuccessful = false; + TcpClient tcpclient = asyncresult.AsyncState as TcpClient; + + if (tcpclient.Client != null) + { + tcpclient.EndConnect(asyncresult); + IsConnectionSuccessful = true; + } + } + catch (Exception ex) + { + IsConnectionSuccessful = false; + socketexception = ex; + } + finally + { + TimeoutObject.Set(); + } + } + } + + + class Tools + { + public static long currentMillis() + { + return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; + } + + /// + /// 将16进制转换成10进制 + /// + /// 16进制字符串 + /// + public static int convertToIntBy16(String str) + { + try + { + return Convert.ToInt32(str, 16); + } + catch (Exception e) + { + + } + return 0; + + } + public static String getCurrentPath(String url) + { + int index = url.LastIndexOf("/"); + + if (index != -1) + { + return url.Substring(0, index) + "/"; + } + else + { + return ""; + } + } + + + /// + /// 将字符串转换成数字,错误返回0 + /// + /// 字符串 + /// + public static int convertToInt(String str) + { + + try + { + return int.Parse(str); + } + catch (Exception e) + { + UnityEngine.Debug.Log("info:-" + e.Message); + } + return 0; + + } + + } +} \ No newline at end of file diff --git a/UnityWaitOSRequest.cs b/UnityWaitOSRequest.cs new file mode 100644 index 0000000..911424d --- /dev/null +++ b/UnityWaitOSRequest.cs @@ -0,0 +1,19 @@ +using OnlySocketHttp; +using UnityEngine; + +class UnityWaitOSRequest : CustomYieldInstruction +{ + RequestAsync mReqAsync; + public UnityWaitOSRequest(RequestAsync reqAsync) + { + mReqAsync = reqAsync; + } + ~UnityWaitOSRequest() + { + mReqAsync = null; + } + public override bool keepWaiting + { + get { return !mReqAsync.isDone; } + } +} \ No newline at end of file