123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- 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;
- /// <summary>
- /// 异步版本HTTP请求.
- /// author: gwang
- /// version:
- /// 2.0.0 队列逻辑修改, 不再用任务等待. 2022.9.7
- /// 1.0.0 task等待异步版. 2022.8.15
- /// </summary>
- public class HttpHelper : MonoSingleton<HttpHelper>
- {
- HttpClient m_client = null;
- // 发送队列
- private ConcurrentQueue<NetPacket> mPacketQueue = new ConcurrentQueue<NetPacket>();
- /// <summary>
- /// 接收队列
- /// </summary>
- private ConcurrentDictionary<int, NetPacket> mRecvQueue = new ConcurrentDictionary<int, NetPacket>();
- 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<string> 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);
- }
- }
- /// <summary>
- /// 异步发送请求
- /// </summary>
- /// <typeparam name="T">回调函数参数类型</typeparam>
- /// <param name="param">请求参数</param>
- /// <param name="callback">回调函数</param>
- public async void SendMsg<T>(ReqVo param, Action<T> callback)
- {
- await Task.Run(async () =>
- {
- var netPacket = NetPacket.CreateNew<T>(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<RespVo>(@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}");
- // 处理失败, 再处理
- }
- }
- /// <summary>
- /// 发送返回结果事件
- /// </summary>
- /// <param name="packet"></param>
- /// <exception cref="UnityException"></exception>
- 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<EventComponent>();
- //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("网络模块故障, 需要应用上层逻辑处理或重置网络.");
- }
- }
- /// <summary>
- /// 退出游戏
- /// </summary>
- 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<T>(ReqVo req, Action<T> 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<T>());
- }
- }
- };
- obj.tsRequest = DateTimeOffset.Now;
- return obj;
- }
- private static volatile int objId = 0;
- private NetPacket()
- {
- }
- }
- }
|