123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- const sproto = require('sproto');
- const SocketUtil = require('./SocketUtil');
- cc.Class({
- name: 'SocketClient',
- properties: {
- _clientHost: undefined,
- _clientSender: undefined,
- _ws: undefined,
- _session: 0,
- _reconnectTimes: 0,
- reconnectMaxTimes: 3,
- _connectTimeoutId: undefined,
- connectTimeout: 10000,
- host: undefined,
- _requestCmds: undefined,
- _requestModels: undefined,
- _responseHandlers: undefined,
- keepalive: 'h5user_keepalive'
- },
- ctor() {
- this._requestCmds = {};
- this._requestModels = {};
- this._responseHandlers = {};
- },
- /**
- * 加载协议文件
- *
- * @author Wetion
- * @date 2019-03-22
- * @param {Array} protoPaths
- */
- loadProtoFiles(protoPaths) {
- if (SocketUtil.clientHost && SocketUtil.clientSender) {
- G.LogUtils.warn('Warn:SocketClient协议文件已经加载过');
- return;
- }
- if (!G.FuncUtils.isArray(protoPaths)) {
- G.LogUtils.warn('Warn:SocketClient加载协议文件参数不正确');
- return;
- }
- // 加载协议文件
- cc.loader.loadResArray(protoPaths, (err, assets) => {
- if (err) {
- G.LogUtils.error('Bug:SocketClient协议文件失败 ' + err);
- return;
- }
- // 做个异步处理
- let protos = {};
- assets.forEach(proto => {
- let schema = JSON.parse(proto.text);
- let data = sproto.createNew(new Uint8Array(schema));
- protos[proto._name] = data;
- });
- SocketUtil.clientHost = protos.socket_s2c.host();
- SocketUtil.clientSender = SocketUtil.clientHost.attach(protos.socket_c2s);
- G.LogUtils.log('Info:SocketClient协议文件加载成功');
- cc.game.emit('e_nwk_socket_sproto_done');
- // 释放资源
- for (const path of protoPaths) {
- cc.loader.releaseRes(path);
- }
- });
- },
- /**
- * 是否准备完成(发送消息的前提)
- *
- * @author Wetion
- * @date 2019-03-22
- * @returns {boolean}
- */
- isReadyDone() {
- return this._ws && this._ws.readyState === WebSocket.OPEN && SocketUtil.clientHost && SocketUtil.clientSender;
- },
- _isValidURLArgs(host, port) {
- return (G.FuncUtils.isIP(host) && G.FuncUtils.isPort(port) || G.FuncUtils.isDomain(host));
- },
- /**
- * 连接Socket
- *
- * @author Wetion
- * @date 2019-03-22
- * @param {String} host
- * @param {Number} port
- */
- connect(host, port) {
- if (!this._isValidURLArgs(host, port)) {
- G.LogUtils.error('Bug:SocketClient连接信息错误');
- return;
- }
- if (this._ws) {
- // 关闭旧连接
- this.close();
- }
- // 连接服务器
- this.host = host;
- this.port = port;
- let url = cc.js.formatStr('ws://%s:%s', host, port);
- if (G.FuncUtils.isIP(host)) {
- url = cc.js.formatStr('ws://%s:%s', host, port);
- } else {
- url = cc.js.formatStr('wss://%s', host);
- }
- url = cc.js.formatStr('ws://%s:%s', host, port);
- this._ws = new WebSocket(url);
- this._ws.binaryType = 'arraybuffer';
- this._ws.onopen = this._onConnect.bind(this);
- this._ws.onmessage = this._onMessage.bind(this);
- this._ws.onerror = this._onError.bind(this);
- this._ws.onclose = this._onClose.bind(this);
- },
- _send(protoName, info, session) {
- let requestBuffer = G.FuncUtils.uint8ToBuffer(SocketUtil.clientSender(protoName, info, session));
- this._ws.send(requestBuffer); // 发送数据
- },
- reconnect() {
- this.connect(this.host, this.port);
- },
- /**
- * 关闭Socket,不管处于什么状态
- */
- close() {
- if (!this._ws) {
- return;
- }
- this._ws.onopen = undefined;
- this._ws.onmessage = undefined;
- this._ws.onerror = undefined;
- this._ws.onclose = undefined;
- if (this._ws.readyState == WebSocket.CONNECTING || this._ws.readyState == WebSocket.OPEN) {
- this._ws.close();
- cc.game.emit('e_socket_on_closed', 'call_close');
- }
- this._ws = undefined;
- },
- /**
- * 客户端请求服务器信息
- *
- * @author Wetion
- * @date 2019-03-22
- * @param {String} protoName
- * @param {Object} info
- * @param {Function} responseHandler
- * @returns {Boolean} 是否成功发送
- */
- c2sRequest(protoName, info, responseHandler) {
- if (!this.isReadyDone()) {
- return;
- }
- this._session++;
- this._requestCmds[this._session] = protoName;
- this._requestModels[this._session] = info;
- this._responseHandlers[this._session] = responseHandler;
- if (protoName !== this.keepalive) {
- G.LogUtils.log('--> 网络请求 socket session:', this._session, 'proto name:', protoName, ' request info:', info);
- }
- this._send(protoName, info, this._session); // 发送数据
- return true;
- },
- c2sResponse(buffer) {
- let protoName = this._requestCmds[buffer.session];
- if (protoName !== this.keepalive) {
- G.LogUtils.log('<-- 网络返回 socket session:', buffer.session, 'proto name:', protoName, ' response info:', buffer.result);
- }
- let requestInfo = this._requestModels[buffer.session];
- let responseHandler = this._responseHandlers[buffer.session];
- if (responseHandler) {
- responseHandler({ requestInfo: requestInfo, responseInfo: buffer.result });
- }
- },
- s2cRequest(buffer) {
- G.LogUtils.log('<~~ 网络通知 socket pname:', buffer.pname, ' request info:', buffer.result);
- cc.game.emit(buffer.pname, buffer.result);
- },
- _onConnect(evt) {
- G.LogUtils.log('连接成功 ', evt);
- cc.game.emit('e_socket_on_connected');
- },
- _onMessage(evt) {
- // 解析数据
- let buffer = SocketUtil.clientHost.dispatch(G.FuncUtils.bufferToUint8(evt.data));
- if (buffer.type === 'REQUEST') {
- // 避免native释放buffer导致 object already destroyed
- buffer.result = G.FuncUtils.clone(buffer.result);
- this.s2cRequest(buffer);
- } else if (buffer.type === 'RESPONSE') {
- // 避免native释放buffer导致 object already destroyed
- buffer.result = G.FuncUtils.clone(buffer.result);
- this.c2sResponse(buffer);
- } else {
- G.LogUtils.warn('不能解析的类型', buffer);
- }
- },
- _onError(err) {
- G.LogUtils.log('连接错误:', err);
- if (this._ws) {
- // 因为onerror后会马上触发onclose
- // 避免发两次 e_socket_on_closed 消息
- this._ws.onopen = undefined;
- this._ws.onmessage = undefined;
- this._ws.onerror = undefined;
- this._ws.onclose = undefined;
- this._ws = undefined;
- }
- cc.game.emit('e_socket_on_closed', 'on_error');
- },
- _onClose(evt) {
- G.LogUtils.log('连接关闭:', evt);
- if (this._ws) {
- // 收到关闭消息后对当前连接进行清理
- this._ws.onopen = undefined;
- this._ws.onmessage = undefined;
- this._ws.onerror = undefined;
- this._ws.onclose = undefined;
- this._ws = undefined;
- }
- cc.game.emit('e_socket_on_closed', 'on_close');
- }
- });
|