Skip to content

介绍

一个通用的 websocket 封装实例,包括 Api 的回调,消息发送,连接关闭。连接出错重连,重连次数限制,重连超时停止操作等

实现

javascript
function setDefaultConfig(defaultConfig, originalConfig) {
	var defaultKeys = Object.keys(defaultConfig);
	var originalKeys = Object.keys(originalConfig);

	if (typeof $ == 'undefined') {
		console.error('请在jQuery之后引用此js');
		return;
	}

	var returnConfig = $.extend(true, {}, originalConfig);

	for (var key in defaultConfig) {
		if (defaultConfig.hasOwnProperty(key)) {
			var element = defaultConfig[key];
			if ($.inArray(key, originalKeys) == -1) {
				switch ($.type(element)) {
					case 'string':
					case 'number':
					case 'boolean':
					case 'function':
						returnConfig[key] = element;
						break;
					case 'array':
						returnConfig[key] = $.extend(true, [], element);
						break;
					case 'object':
						returnConfig[key] = $.extend(true, {}, element);
						break;
					case 'undefined':
					case 'null':
						break;
					default:
						break;
				}
			}
		}
	}

	return returnConfig;
}

/**
 *
 * @param {配置} originalConfig
 * @param {string} originalConfig.url - 链接的url
 * @param {string} originalConfig.host - 主机名	有url则不传,url需要有主机名
 * @param {string} originalConfig.protocols - 链接使用的协议 ws wss 默认根据location进行判断 http ws or htps wss 有url则不传,url需要有协议
 * @param {string} originalConfig.port - 例 ':8080' 端口号 有url则不传,url需要有端口
 * @param {string} originalConfig.message - 连接成功后默认发送的数据,并且在执行send如果没有传递参数,则默认发送该值
 * @param {Function} originalConfig.onopen - 连接成功后触发发
 * @param {Function} originalConfig.onmessage - 接收到消息触发
 * @param {Function} originalConfig.onclose - 关闭后触发
 * @param {Function} originalConfig.onbeforeclose - 关闭之前触发
 * @param {Function} originalConfig.onerror - 发生错误后触发
 */
var wbst = (function (window, $) {
	//重连开始时间
	var reConnectBeginTime;
	//重连计数
	var reConnectNum = 1;
	//重连次数限定
	var limitReConnectNum = 3;
	//重连timer
	var reConnectTimer;
	// 构造函数
	function websocket(originalConfig) {
		/**
		 * Wbst实例     close   //关闭连接
		 *              send    //发送消息
		 */
		//默认配置参数
		var defaultConfig = {
			protocol: location.protocol == 'http:' ? 'ws:' : 'wss:',
			port: location.port,
			ws: null,
			timeout: 5000,
			onopen: function () {},
			onmessage: function () {},
			onclose: function () {},
			onerror: function () {},
			onbeforeclose: function () {},
		};
		//主机名
		var host = location.hostName;
		//路径
		var pathName = location.pathname;
		//是否心跳状态,为false不可操作,等待重连
		this.isHeartFlat = false;
		//重连状态
		this.isReconnect = false;

		originalConfig
			? (this.config = setDefaultConfig(defaultConfig, originalConfig))
			: (this.config = defaultConfig);

		this.config.protocol.indexOf(':') == -1 ? (config.protocol += ':') : '';
		//如果没有定义url,则拼接url
		if (typeof this.config.url == 'undefined') {
			this.config.url = this.config.protocol + '//' + host + ':' + this.config.port + pathName;
		}

		//发送消息函数
		this.send = function (message) {
			if (!this.ws) {
				console.error('websocket链接已关闭', this);
				return;
			}
			//如果没有传值,则默认发送配置的默认消息
			if (typeof message == 'undefined' && typeof this.config.message != 'undefined') {
				message = this.config.message;
			}
			//判断心跳检测,连接是否存在或已连接
			if (!this.isHeartFlat) {
				// console.error("websocket未连接或出现故障或已断开,正在重新发送...");
				var _self = this;
				// var timeout = setTimeout(function () {}, 0);
				var timeout = setTimeout(function () {
					var interval = setInterval(function () {
						if (_self.ws.readyState === 1) {
							clearTimeout(timeout);
							clearInterval(interval);
							timeout = null;
							interval = null;
							_self.send(message);
							// console.log("websocket已发送请求");
						} else {
							clearTimeout(timeout);
							clearInterval(interval);
							console.error('websocket链接异常', this);
						}
					}, 100);
				}, 0);
				return;
			} else {
				this.ws.send(message);
			}
		};
		//关闭连接
		this.close = function () {
			if (!this.ws) {
				console.error('链接已关闭');
				return;
			}
			this.config.onbeforeclose(this.config, this.ws);
			this.ws.close();
		};

		this.init();
	}
	//初始化
	websocket.prototype.init = function () {
		var _self = this;

		window.WebSocket = window.WebSocket || window.MozWebSocket;
		if (!window.WebSocket) {
			console.error('浏览器不支持WebSocket');
			return;
		}

		var ws = new WebSocket(this.config.url);
		ws.onopen = function (e) {
			_self.onopen(e);
		};
		ws.onmessage = function (e) {
			_self.onmessage(e);
		};
		ws.onclose = function (e) {
			_self.onclose(e);
		};
		ws.onerror = function (e) {
			_self.onerror(e);
		};

		this.ws = ws;
		return;
	};
	//连接成功
	websocket.prototype.onopen = function (e) {
		this.isHeartFlat = true;
		this.config.onopen(e);
		if (typeof this.config.message != 'undefined') {
			this.ws.send(this.config.message);
		}
	};
	//接收消息回调
	websocket.prototype.onmessage = function (e) {
		this.config.onmessage(e);
	};
	//连接出错回调,默认执行重连操作
	websocket.prototype.onerror = function (e) {
		var _self = this;

		//心跳值为false
		this.isHeartFlat = false;
		this.config.onerror(e);

		if (reConnectNum <= limitReConnectNum) {
			//开始重连
			console.error('WebSocket连接出错,正在重连...');
			clearTimeout(reConnectTimer);
			reConnectTimer = setTimeout(function () {
				if (reConnectNum == 1) {
					reConnectBeginTime = new Date();
				}

				console.log('尝试第' + reConnectNum + '次重连');
				_self.isReconnect = true;
				_self.reConnect();

				//如果心跳值为true,则重连成功
				if (_self.isHeartFlat) {
					clearTimeout(reConnectTimer);
					reConnectTimer = null;
				}
			}, 2000);
		} else {
			console.error('重连次数超出设定值,不再重连,请检查配置项');
		}
	};
	//关闭回调
	websocket.prototype.onclose = function (e) {
		this.isHeartFlat = false;
		this.config.onclose(e);
	};
	//重连操作
	websocket.prototype.reConnect = function () {
		//如果没有触发重连操作,则不执行
		if (!this.isReconnect) return;

		//判断重连超时,若超时,则不再重连
		if (new Date() - reConnectBeginTime > this.config.timeout) {
			console.error('websocket重连超时');
			this.isReconnect = false;
			//清除重连延时器
			clearTimeout(reConnectTimer);
			reConnectTimer = null;
			return;
		}

		//开始重连
		var _self = this;
		//重连计数器
		reConnectNum++;
		_self.init();
	};
	return websocket;
})(window, jQuery);