HttpClient.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. const sproto = require('sproto');
  2. const HttpUtil = require('./HttpUtil');
  3. cc.Class({
  4. name: 'HttpClient',
  5. properties: {
  6. _clientHost: undefined,
  7. _clientSender: undefined,
  8. _session: 0,
  9. _host: undefined,
  10. _port: undefined,
  11. _path: '',
  12. method: 'GET',
  13. secretCode: '',
  14. timeout: 10000
  15. },
  16. /**
  17. * 加载协议文件
  18. *
  19. * @author Wetion
  20. * @date 2019-03-22
  21. * @param {Array} protoPaths
  22. */
  23. loadProtoFiles (protoPaths) {
  24. if (HttpUtil.clientHost && HttpUtil.clientSender) {
  25. G.LogUtils.warn('Warn:HttpClient协议文件已经加载过');
  26. return;
  27. }
  28. if (!G.FuncUtils.isArray(protoPaths)) {
  29. G.LogUtils.warn('Warn:HttpClient加载协议文件参数不正确');
  30. return;
  31. }
  32. // 加载协议文件
  33. cc.loader.loadResArray(protoPaths, (err, assets) => {
  34. if (err) {
  35. G.LogUtils.error('Bug:HttpClient协议文件失败 ' + err);
  36. return;
  37. }
  38. let protos = {};
  39. assets.forEach(proto => {
  40. let schema = JSON.parse(proto.text);
  41. let data = sproto.createNew(new Uint8Array(schema));
  42. protos[proto._name] = data;
  43. });
  44. HttpUtil.init();
  45. HttpUtil.clientHost = protos.http_s2c.host();
  46. HttpUtil.clientSender = HttpUtil.clientHost.attach(protos.http_c2s);
  47. G.LogUtils.log('Info:HttpClient协议文件加载成功');
  48. cc.game.emit('e_nwk_http_sproto_done');
  49. // 释放资源
  50. for (const path of protoPaths) {
  51. cc.loader.releaseRes(path);
  52. }
  53. });
  54. },
  55. /**
  56. * 是否准备完成(发送消息的前提)
  57. *
  58. * @author Wetion
  59. * @date 2019-03-22
  60. * @returns {Boolean}
  61. */
  62. isReadyDone () {
  63. return HttpUtil.clientHost && HttpUtil.clientSender;
  64. },
  65. /**
  66. * 设置URL参数
  67. *
  68. * @author Wetion
  69. * @date 2019-03-28
  70. * @param {String} host
  71. * @param {Number} port
  72. * @param {String} path
  73. */
  74. setURLArgs (host, port, path) {
  75. this._host = host;
  76. this._port = port;
  77. this._path = path || '';
  78. },
  79. _isValidURLArgs () {
  80. return (G.FuncUtils.isIP(this._host) && G.FuncUtils.isPort(this._port) || G.FuncUtils.isDomain(this._host));
  81. },
  82. /**
  83. * 客户端请求服务器信息
  84. *
  85. * @author Wetion
  86. * @date 2019-03-22
  87. * @param {String} protoName
  88. * @param {Object} info
  89. * @param {Function} responseHandler
  90. */
  91. c2sRequest (protoName, info, responseHandler) {
  92. if (!this.isReadyDone()) {
  93. G.LogUtils.warn('Warn:HttpClient还未准备好');
  94. return;
  95. }
  96. if (!this._isValidURLArgs() || !protoName || !info) {
  97. G.LogUtils.error('Bug:HttpClient发送请求信息错误');
  98. return;
  99. }
  100. this._session++;
  101. let requestBuffer = G.FuncUtils.uint8ToBuffer(HttpUtil.clientSender(protoName, info, this._session));
  102. let sourceStr = G.FuncUtils.bufferToString(requestBuffer) + this.secretCode;
  103. let md5Str = MD5.hex(sourceStr);
  104. let url;
  105. if (G.FuncUtils.isIP(this._host)) {
  106. url = cc.js.formatStr('http://%s:%s/%s?sign=%s', this._host, this._port, this._path, md5Str);
  107. } else {
  108. url = cc.js.formatStr('https://%s/%s?sign=%s', this._host, this._path, md5Str);
  109. }
  110. let xhr = cc.loader.getXMLHttpRequest();
  111. xhr.onreadystatechange = () => {
  112. // 查看readyState其他值 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/readyState
  113. if (xhr.readyState === 4 && xhr.status == 200) {
  114. let responseInfo;
  115. try {
  116. // 解析数据
  117. let responseBuffer = HttpUtil.clientHost.dispatch(G.FuncUtils.bufferToUint8(xhr.response));
  118. // 避免native释放buffer导致 object already destroyed
  119. responseInfo = G.FuncUtils.clone(responseBuffer.result);
  120. G.LogUtils.log('\n<-- 网络返回 http session:', responseBuffer.session, 'proto name:', protoName, ' response info:', responseBuffer.result, '\n');
  121. } catch (error) {
  122. // 返回解析协议错误码
  123. responseInfo = {code: 0};
  124. // 解析数据异常
  125. let responseText = G.FuncUtils.bufferToString(xhr.response);
  126. try {
  127. // http的错误码是以JSON的形式存在
  128. let responseBuffer = JSON.parse(responseText);
  129. let errorCodeDesc = this._getXHRErrorCodeDesc(responseBuffer.code);
  130. G.LogUtils.error('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' http access error info:', errorCodeDesc + responseText, '\n');
  131. } catch (e) {
  132. // 如果解析JSON错误,那么这应该是数据有问题导致sproto解析不了
  133. G.LogUtils.error('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' sproto parse:', responseText, 'error\n');
  134. G.LogUtils.error(error);
  135. }
  136. }
  137. if (responseHandler) {
  138. responseHandler({requestInfo: xhr.requestInfo, responseInfo: responseInfo});
  139. } else {
  140. G.LogUtils.warn('Warn:HttpClient没有定义响应回调函数');
  141. }
  142. }
  143. };
  144. xhr.ontimeout = ()=> {
  145. G.LogUtils.warn('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' ontimeout');
  146. if (responseHandler) {
  147. responseHandler({requestInfo: xhr.requestInfo, responseInfo: {code: 400} });
  148. }
  149. };
  150. xhr.onerror = ()=> {
  151. G.LogUtils.warn('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' onerror');
  152. if (responseHandler) {
  153. responseHandler({requestInfo: xhr.requestInfo, responseInfo: {code: 400} });
  154. }
  155. };
  156. xhr.onabort = ()=> {
  157. G.LogUtils.warn('\n<-- 网络返回 http session:', xhr.session, 'proto name:', protoName, ' onabort');
  158. if (responseHandler) {
  159. responseHandler({requestInfo: xhr.requestInfo, responseInfo: {code: 400} });
  160. }
  161. };
  162. xhr.timeout = this.timeout;
  163. xhr.requestInfo = info;
  164. xhr.session = this._session;
  165. xhr.responseType = 'arraybuffer';
  166. xhr.open(this.method, url);
  167. G.LogUtils.log('--> 网络请求 http session:', this._session, 'proto name:', protoName, ' request info:', info, 'url:', url);
  168. xhr.send(requestBuffer); // Uint8Array is an ArrayBufferView
  169. },
  170. _getXHRErrorCodeDesc (code) {
  171. let desc = '';
  172. if (code == 500) {
  173. desc = '服务器数据校验失败';
  174. } else {
  175. desc = '没有错误码' + code + '的描述';
  176. }
  177. return desc;
  178. }
  179. });