ssocket-js
Version:
仿 Koa 中间件控制的 WebSocket 服务的 Web 客户端程序
220 lines (219 loc) • 9.05 kB
JavaScript
;
/*
* @Author: Summer
* @LastEditors: Summer
* @Description: 客户端程序
* @LastEditTime: 2021-05-10 17:35:35 +0800
* @FilePath: /ssocket-js/src/index.ts
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var code_1 = __importStar(require("./code")), Code = code_1;
var emitter_1 = __importDefault(require("./emitter"));
var logger_1 = __importDefault(require("./logger"));
var logger = logger_1.default("swebsocket");
var socket;
var __index__ = 0;
/**
* 发送握手包
* @param id 客户端ID
* @param ack 握手ID
*/
function shakehands(id, ack) {
if (socket && socket.readyState != WebSocket.OPEN)
return;
socket.send(Code.encode(Code.PackageType.shakehands, { id: id, ack: ack }));
logger(id + ":shakehands", { ack: ack });
}
/**
* 发送消息
* @param id 客户端ID
* @param data JSON 数据包
*/
function send(id, data) {
if (!data.path)
return logger(id + ":send", "Cannot have the path field");
if (socket && socket.readyState != WebSocket.OPEN)
return;
socket.send(Code.encode(Code.PackageType.data, data));
logger(id + ":send", data);
}
module.exports = /** @class */ (function (_super) {
__extends(Ssocket, _super);
/**
* 构造一个 Ssocket 连接
* @param url ws/wss 连接地址
* @param opts 配置
*/
function Ssocket(url, opts) {
var _this = _super.call(this) || this;
_this.url = url;
/**客户端连接状态 */
_this.status = Code.SocketStatus.CLOSE;
_this.opts = Object.assign({
ping_timeout: 1000 * 11,
ping_time: 1000 * 9,
reconnection_count: Number.MAX_VALUE,
reconnection_time: 1000 * 2,
}, opts);
_this.id = "";
_this.ping_timeout_id = 0;
_this.reconnection_id = 0;
socket = null;
_this.reconnection_count = _this.opts.reconnection_count;
if (_this.opts.protos) {
if (_this.opts.protos.request)
Code.parseRequestJson(_this.opts.protos.request);
if (_this.opts.protos.response)
Code.parseResponseJson(_this.opts.protos.response);
}
_this.connection();
logger(_this.id + ":constructor", { opts: opts });
return _this;
}
Object.defineProperty(Ssocket.prototype, "socket", {
/**原生 Socket 对象 */
get: function () { return socket; },
enumerable: false,
configurable: true
});
Ssocket.prototype.getid = function () { return this.id; };
Ssocket.prototype.getStatus = function () { return this.status; };
/**
* 手动发送重连
*/
Ssocket.prototype.connection = function () {
var _this = this;
if (this.status != Code.SocketStatus.CLOSE)
return;
socket = new WebSocket(this.url);
socket.binaryType = "arraybuffer";
socket.onopen = function (ev) {
clearTimeout(_this.reconnection_id);
_this.emit("open", ev);
_this.status = Code.SocketStatus.OPEN;
_this.reconnection_count = _this.opts.reconnection_count;
shakehands(_this.id, Code.SocketStatus.SHAKING_HANDS);
_this.emit("shakehands", _this.status = Code.SocketStatus.SHAKING_HANDS);
logger(_this.id + ":open", { status: _this.status });
};
socket.onclose = function (_a) {
var code = _a.code, reason = _a.reason;
clearTimeout(_this.ping_timeout_id);
_this.status = Code.SocketStatus.CLOSE;
socket.onopen = socket.onmessage = socket.onclose = socket.onerror = null;
socket = null;
if (--_this.reconnection_count >= 0) {
_this.reconnection_id = setTimeout(function () {
_this.connection();
_this.emit("reconnectioning", _this.opts.reconnection_count - _this.reconnection_count);
_this.status = Code.SocketStatus.RECONNECTION;
logger(_this.id + ":reconnectioning", { status: _this.status });
}, _this.opts.reconnection_time);
}
_this.emit("close", { code: code, reason: reason });
logger(_this.id + ":close", { code: code, reason: reason });
};
socket.onerror = function (ev) { return _this.emit("error", ev); };
socket.onmessage = function (ev) {
var data = Code.decode(ev.data);
logger(_this.id + ":message", data);
if (data.type == Code.PackageType.shakehands) {
if (data.ack == Code.SocketStatus.HANDSHAKE) {
if (_this.id == "")
_this.id = data.id;
shakehands(_this.id, Code.SocketStatus.CONNECTION);
_this.emit("shakehands", _this.status = Code.SocketStatus.HANDSHAKE);
}
else if (data.ack == Code.SocketStatus.CONNECTION) {
_this.status = Code.SocketStatus.CONNECTION;
_this.emit("shakehands", _this.status = Code.SocketStatus.CONNECTION);
_this.emit(_this.id != data.id ? "reconnection" : "connection");
socket.send(Code.encode(Code.PackageType.heartbeat));
}
}
else if (data.type == Code.PackageType.heartbeat) {
_this.emit("pong", data.data);
clearTimeout(_this.ping_timeout_id);
setTimeout(function () {
if (_this.status != Code.SocketStatus.CONNECTION)
return;
socket.send(Code.encode(Code.PackageType.heartbeat));
_this.ping_timeout_id = setTimeout(function (_) { return socket.close(code_1.default[4102][0], code_1.default[4102][1]); }, _this.opts.ping_timeout);
_this.emit("ping");
logger(_this.id + ":heartbeat");
}, _this.opts.ping_time);
}
else if (data.type == Code.PackageType.data) {
if (data.request_id)
_this.emit(data.request_id, data);
else
_this.emit(data.path, data);
}
};
};
/**
* 发起一次消息请求
* @param path 请求路径
* @param data 携带数据
* @param fn 回调函数,非必传
*/
Ssocket.prototype.request = function (path, data, fn) {
if (this.status != Code.SocketStatus.CONNECTION)
return;
var request_data = { path: path, data: data };
if (fn) {
request_data.request_id = __index__++ > 999999 ? (__index__ = 1) : __index__;
this.once(String(request_data.request_id), fn);
}
try {
send(this.id, request_data);
}
catch (error) {
this.emit.apply(this, __spreadArrays(["close", this.getid()], code_1.default[4101]));
this.status = Code.SocketStatus.CLOSE;
logger(this.id + ":send", error);
}
};
return Ssocket;
}(emitter_1.default));