const sproto = require('sproto'); const HttpUtil = require('./HttpUtil'); cc.Class({ name: 'HttpClient', properties: { _clientHost: undefined, _clientSender: undefined, _session: 0, _host: undefined, _port: undefined, _path: '', method: 'GET', secretCode: '', timeout: 10000 }, /** * 加载协议文件 * * @author Wetion * @date 2019-03-22 * @param {Array} protoPaths */ loadProtoFiles (protoPaths) { if (HttpUtil.clientHost && HttpUtil.clientSender) { G.LogUtils.warn('Warn:HttpClient协议文件已经加载过'); return; } if (!G.FuncUtils.isArray(protoPaths)) { G.LogUtils.warn('Warn:HttpClient加载协议文件参数不正确'); return; } // 加载协议文件 cc.loader.loadResArray(protoPaths, (err, assets) => { if (err) { G.LogUtils.error('Bug:HttpClient协议文件失败 ' + err); return; } let protos = {}; assets.forEach(proto => { let schema = JSON.parse(proto.text); let data = sproto.createNew(new Uint8Array(schema)); protos[proto._name] = data; }); HttpUtil.init(); HttpUtil.clientHost = protos.http_s2c.host(); HttpUtil.clientSender = HttpUtil.clientHost.attach(protos.http_c2s); G.LogUtils.log('Info:HttpClient协议文件加载成功'); cc.game.emit('e_nwk_http_sproto_done'); // 释放资源 for (const path of protoPaths) { cc.loader.releaseRes(path); } }); }, /** * 是否准备完成(发送消息的前提) * * @author Wetion * @date 2019-03-22 * @returns {Boolean} */ isReadyDone () { return HttpUtil.clientHost && HttpUtil.clientSender; }, /** * 设置URL参数 * * @author Wetion * @date 2019-03-28 * @param {String} host * @param {Number} port * @param {String} path */ setURLArgs (host, port, path) { this._host = host; this._port = port; this._path = path || ''; }, _isValidURLArgs () { return (G.FuncUtils.isIP(this._host) && G.FuncUtils.isPort(this._port) || G.FuncUtils.isDomain(this._host)); }, /** * 客户端请求服务器信息 * * @author Wetion * @date 2019-03-22 * @param {String} protoName * @param {Object} info * @param {Function} responseHandler */ c2sRequest (protoName, info, responseHandler) { if (!this.isReadyDone()) { G.LogUtils.warn('Warn:HttpClient还未准备好'); return; } if (!this._isValidURLArgs() || !protoName || !info) { G.LogUtils.error('Bug:HttpClient发送请求信息错误'); return; } this._session++; let requestBuffer = G.FuncUtils.uint8ToBuffer(HttpUtil.clientSender(protoName, info, this._session)); let sourceStr = G.FuncUtils.bufferToString(requestBuffer) + this.secretCode; let md5Str = MD5.hex(sourceStr); let url; if (G.FuncUtils.isIP(this._host)) { url = cc.js.formatStr('http://%s:%s/%s?sign=%s', this._host, this._port, this._path, md5Str); } else { url = cc.js.formatStr('https://%s/%s?sign=%s', this._host, this._path, md5Str); } let xhr = cc.loader.getXMLHttpRequest(); xhr.onreadystatechange = () => { // 查看readyState其他值 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/readyState if (xhr.readyState === 4 && xhr.status == 200) { let responseInfo; try { // 解析数据 let responseBuffer = HttpUtil.clientHost.dispatch(G.FuncUtils.bufferToUint8(xhr.response)); // 避免native释放buffer导致 object already destroyed responseInfo = G.FuncUtils.clone(responseBuffer.result); G.LogUtils.log('\n<-- 网络返回 http session:', responseBuffer.session, 'proto name:', protoName, ' response info:', responseBuffer.result, '\n'); } catch (error) { // 返回解析协议错误码 responseInfo = {code: 0}; // 解析数据异常 let responseText = G.FuncUtils.bufferToString(xhr.response); try { // http的错误码是以JSON的形式存在 let responseBuffer = JSON.parse(responseText); let errorCodeDesc = this._getXHRErrorCodeDesc(responseBuffer.code); G.LogUtils.error('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' http access error info:', errorCodeDesc + responseText, '\n'); } catch (e) { // 如果解析JSON错误,那么这应该是数据有问题导致sproto解析不了 G.LogUtils.error('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' sproto parse:', responseText, 'error\n'); G.LogUtils.error(error); } } if (responseHandler) { responseHandler({requestInfo: xhr.requestInfo, responseInfo: responseInfo}); } else { G.LogUtils.warn('Warn:HttpClient没有定义响应回调函数'); } } }; xhr.ontimeout = ()=> { G.LogUtils.warn('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' ontimeout'); if (responseHandler) { responseHandler({requestInfo: xhr.requestInfo, responseInfo: {code: 400} }); } }; xhr.onerror = ()=> { G.LogUtils.warn('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' onerror'); if (responseHandler) { responseHandler({requestInfo: xhr.requestInfo, responseInfo: {code: 400} }); } }; xhr.onabort = ()=> { G.LogUtils.warn('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' onabort'); if (responseHandler) { responseHandler({requestInfo: xhr.requestInfo, responseInfo: {code: 400} }); } }; xhr.timeout = this.timeout; xhr.requestInfo = info; xhr.session = this._session; xhr.responseType = 'arraybuffer'; xhr.open(this.method, url); G.LogUtils.log('--> 网络请求 http session:', this._session, 'proto name:', protoName, ' request info:', info, 'url:', url); xhr.send(requestBuffer); // Uint8Array is an ArrayBufferView }, _getXHRErrorCodeDesc (code) { let desc = ''; if (code == 500) { desc = '服务器数据校验失败'; } else { desc = '没有错误码' + code + '的描述'; } return desc; } });