using CSharpUtil; using GameFramework.Network; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Concurrent; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using UnityEngine; using UnityGameFramework.Runtime; /// /// 异步版本HTTP请求. /// author: gwang /// version: /// 2.0.0 队列逻辑修改, 不再用任务等待. 2022.9.7 /// 1.0.0 task等待异步版. 2022.8.15 /// public class HttpHelper : MonoSingleton { HttpClient m_client = null; // 发送队列 private ConcurrentQueue mPacketQueue = new ConcurrentQueue(); /// /// 接收队列 /// private ConcurrentDictionary mRecvQueue = new ConcurrentDictionary(); private volatile int mRecvPackSN = 0; private volatile int mDN = -1; private string m_url = Config_URL.Server_URL; // 服务器地址 private HttpHelper() { #if UNITY_EDITOR bool useProxy = false; if (useProxy) { var msghandler = new HttpClientHandler(); msghandler.UseProxy = true; msghandler.Proxy = new WebProxy("127.0.0.1", 8888); m_client = new HttpClient(msghandler); } else { #endif m_client = new HttpClient(); #if UNITY_EDITOR } #endif m_client.MaxResponseContentBufferSize = 256000000; // 最大返回的字节数 m_client.Timeout = TimeSpan.FromMilliseconds(2500); // 超时时间2.5秒 } // 回调方法 public delegate void OnNetBack(object eventObj); private string Get(string url) { try { var responseString = m_client.GetStringAsync(url); return responseString.Result; } catch (Exception ex) { return null; } } private string Post(string url, string strJson) => PostAsync(url, strJson).Result; // post同步请求方法 private async Task PostAsync(string url, string strJson) // post异步请求方法 { try { byte[] data = CompressUtil.Deflate(GlobalConfig.Encoding.GetBytes(strJson)); // 自定义编码(deflate+utf8) HttpContent content = new ByteArrayContent(data); HttpResponseMessage res = await m_client.PostAsync(url, content); // 由HttpClient发出异步Post请求 if (res.StatusCode == HttpStatusCode.OK) { var bytes = res.Content.ReadAsByteArrayAsync().Result; string decode = CompressUtil.InFlate(bytes, GlobalConfig.Encoding); // 自定义解码(defalte+utf8) return decode; } else { if (res.StatusCode == HttpStatusCode.RequestTimeout) // 请求超时 { LogHelper.LogWarning("HTTP Request time out!"); } var resp = new RespVo() { err = (int)HttpStatusCode.RequestTimeout, tag = new JObject() }; resp.tag["errmsg"] = "请求超时.请检查网络环境!"; return JsonConvert.SerializeObject(resp); } } catch (Exception ex) { Debug.LogException(ex); var resp = new RespVo() { err = (int)HttpStatusCode.SeeOther, tag = new JObject() }; resp.tag["errmsg"] = "网络失败! 请检查网络环境!";// + ex.Message; return JsonConvert.SerializeObject(resp); } } /// /// 异步发送请求 /// /// 回调函数参数类型 /// 请求参数 /// 回调函数 public async void SendMsg(ReqVo param, Action callback) { await Task.Run(async () => { var netPacket = NetPacket.CreateNew(param, callback); var ctx = JsonConvert.SerializeObject(netPacket.request); if (GlobalConfig.GAME_COMM_DEBUG) { var msg = ctx.Length > 1024 ? $"{ctx[..150]}...{ctx[^30..]}" : ctx; LogHelper.Log($"[SEND:]{msg}"); } string res = await this.PostAsync(m_url, ctx); netPacket.tsResp = DateTimeOffset.Now; if (res == null) { netPacket.response = new RespVo() { err = ErrCode.net_other }; } else { if (GlobalConfig.GAME_COMM_DEBUG) { string format = res; var msg = format.Length > 1024 ? format[..150] + $"...({format.Length / 1024:.0kb})..." + format[^30..] : format; // 约简日志长度 var sp = (netPacket.tsResp - netPacket.tsRequest).TotalMilliseconds; // 计算往返耗时 LogHelper.Log($"[RECV:]({sp:.00}ms){msg}"); } RespVo data = JsonConvert.DeserializeObject(@res); // 返回的json数据解析成结构体 netPacket.response = data; } if (!mRecvQueue.TryAdd(netPacket.request.SN, netPacket)) { LogHelper.LogWarning($"收到重复响应包!{netPacket.request.SN}"); } //LogHelper.Log($"放入{netPacket.request.SN}"); }); } private void Update() { while (mRecvQueue.TryRemove(mRecvPackSN, out var netPacket)) { RaiseResult(netPacket); mRecvPackSN++; //LogHelper.Log($"放出 SN: {netPacket.request.SN}"); // 处理失败, 再处理 } } /// /// 发送返回结果事件 /// /// /// private void RaiseResult(NetPacket packet) { if (packet.errNum == 0) { var resp = packet.response; if (resp.err == ErrCode.succeed) { if (!(packet.request.cmd == CmdCode.cmd_user_loginuserinfo || packet.request.cmd == CmdCode.cmd_user_registerNewRole) && resp.result.TryGetValue("store", out var store)) { if (mDN <= resp.DN) { mDN = resp.DN; UserProxy.Instance.player.InitFromStore((JObject)store); } else { LogHelper.LogWarning("DN版本低, 丢弃数据."); } } packet.handler?.Invoke(); // 先执行结果回调 EventComponent eventCmpt = GameEntry.GetComponent(); //if (null != eventCmpt) //{ resp.events.ForEach(ev => { switch (ev.name) { case Enum_EventType.TaskCardFinished: // 任务卡完成dwddd eventCmpt?.FireNow(this, new TaskCardEventFinish(ev.arg1, ev.arg2)); break; case Enum_EventType.TaskCardActived: // 任务卡激活 eventCmpt?.FireNow(this, new TaskCardEventAtive(ev.arg1, ev.arg2)); break; case Enum_EventType.TaskCardReward: // 任务卡领取奖励 eventCmpt?.FireNow(this, new TaskCardEventReward(ev.arg1, ev.arg2)); break; case Enum_EventType.MissionStepComplete: // 任务卡-任务步骤完成 eventCmpt?.FireNow(this, new TaskEventStepFinish(ev.arg1, ev.arg2)); break; case Enum_EventType.MissionStepProcess: // 任务卡 - 进度更新 eventCmpt?.FireNow(this, new TaskEventStepProcess(ev.arg1, ev.arg2)); break; case Enum_EventType.AddItem: // 获得道具 eventCmpt?.FireNow(this, new User_AddItemEvent(ev.arg1, ev.arg2)); break; case Enum_EventType.RemoveItem: // 移除道具 break; case Enum_EventType.AddTaskItem: // 添加任务卡 eventCmpt?.FireNow(this, new TaskCardEventAdd(ev.arg1, ev?.arg2 ?? null)); break; case Enum_EventType.RemoveTaskItem: // 移除任务卡 break; case Enum_EventType.StartPlot: // 开启剧情对话 eventCmpt?.FireNow(this, new StartPlotScene(Convert.ToInt32(ev.arg1), Convert.ToInt32(ev.arg2))); break; case Enum_EventType.NpcDialog: // 开启NPC对话 eventCmpt?.FireNow(this, new StartPlotNPC(Convert.ToInt32(ev.arg1), Convert.ToInt32(ev.arg2))); break; case Enum_EventType.UnlockBuild: UserProxy.Instance.player.PrivateState.unlockedBuild.Add(Convert.ToInt32(ev.arg1)); // 同步已解锁建筑id BuildManager.UnlockBuild(Convert.ToInt32(ev.arg1)); eventCmpt?.FireNow(this, new BuildUnlockEvent(ev.arg1, ev.arg2)); break; case Enum_EventType.UnlockMap: eventCmpt?.FireNow(this, new MapUnlockEvent(ev.arg1, ev.arg2)); break; case Enum_EventType.UserLvlUP: eventCmpt?.FireNow(this, new UserLvlUpEvent(ev.arg1, ev.arg2)); break; case Enum_EventType.HeroLvlUp: eventCmpt?.FireNow(this, new HeroLvlUpEvent(ev.arg1, ev.arg2)); eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name)); break; case Enum_EventType.HeroTupo: eventCmpt?.FireNow(this, new HeroTupoEvent(ev.arg1, ev.arg2)); eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name)); break; case Enum_EventType.WeaponLvUp: eventCmpt?.FireNow(this, new WuqiLvlUpEvent()); eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name)); break; case Enum_EventType.WeaponTuPo: eventCmpt?.FireNow(this, new WuqiTupoEvent()); eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name)); break; case Enum_EventType.YanLingLvUp: eventCmpt?.FireNow(this, new YanLingLvlUpEvent()); eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name)); break; case Enum_EventType.YanLingTuPo: eventCmpt?.FireNow(this, new YanLingTupoEvent()); eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name)); break; case Enum_EventType.PaySuccess: eventCmpt?.FireNow(this, new PaySuccessEvent()); break; } }); //} } else { var errNo = resp.err; var msg = (resp.tag ?? new JObject()).TryGetValue("errmsg", out var msgJ) ? msgJ.ToString() : ""; if (errNo == 3510) // 糊了一个补丁, 任务卡未找到是因为重复发送领取奖励导致的,但是流程上不好处理,-gwang 2021年3月4日17:22:55 { } else if (errNo == 1021) { UI_CueDialog.Instance().Open("消息超时,或者本地设备时间与现实时间差距较大!", "错误", E_DialogType.OneButton, () => { LogHelper.Log("Exiting"); Application.Quit(); }); } if (GameConfigData.IsReady) { // 错误处理模块 if (GameConfigData.Ins.errmsg.ContainsKey(resp.err)) { var errInfo = GameConfigData.Ins.errmsg[resp.err]; if (null != errInfo) { msg = string.IsNullOrEmpty(msg) ? errInfo.msg : msg; if (errInfo.type == 1) // 错误类型为重启 { UI_CueDialog.Instance().Open(msg, "错误", E_DialogType.OneButton, () => { LogHelper.Log("Exiting"); Application.Quit(); }); } else if (errInfo.type == 0) // 错误类型为警告 { UI_TipsWindow.InitFloatWaringDialog("警告:" + msg); } } } } else // 错误信息表尚未初始化 { LogHelper.Log($"Err[{errNo}]:{msg}."); if (msg.Length > 0) { UI_CueDialog.Instance().Open(msg, "错误", E_DialogType.OneButton, () => { LogHelper.Log("Exiting"); Application.Quit(); }); } // UI_TipsWindow.InitFloatWaringDialog(errInfo.type == 1 ? "错误:" : "警告:"+ msg); } } } else { UI_CueDialog.Instance().Open("连接不到游戏服务器, 请检查网络.", "网络故障", E_DialogType.OneButton, ExitGame); throw new UnityException("网络模块故障, 需要应用上层逻辑处理或重置网络."); } } /// /// 退出游戏 /// private void ExitGame() { Application.Quit(); } private class NetPacket { // 包体编号 public int id = 0; // 请求内容 public ReqVo request = null; // 返回回调 public Action handler = null; // 返回内容 public RespVo response = null; // 发送时间 public DateTimeOffset tsRequest = DateTimeOffset.MinValue; // 返回时间 public DateTimeOffset tsResp = DateTimeOffset.MinValue; // 设定的超时时间(秒) public int tsTimeout = 0; // 错误编号 public int errNum = 0; // 错误信息 public string errMsg = string.Empty; public static NetPacket CreateNew(ReqVo req, Action callback) { var obj = new NetPacket(); obj.tsTimeout = GlobalConfig.Net_Connect_TimeOut_sec; obj.id = objId++; obj.request = req; obj.handler = () => { if (null != obj.response) { if (obj.response is T t) // RespVo(某些消息需要直接拿Resp做处理) { callback?.Invoke(t); } else { callback?.Invoke(obj.response.result.ToObject()); } } }; obj.tsRequest = DateTimeOffset.Now; return obj; } private static volatile int objId = 0; private NetPacket() { } } }