HttpClient.js 7.1 KB

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