MDN - WebSocket
    编写 WebSocket 服务器

    1. // Web Scoket 封装类方法
    2. export default class WebSocketClass {
    3. constructor(options) {
    4. // websocket对象
    5. this.ws = null;
    6. // websocket是否关闭
    7. this.socketStatus = null;
    8. // 消息队列,未成功发送的队列
    9. this.socketMsgQueue = [];
    10. // 手动记录socket状态 -1:未连接, 0:连接中, 1:已连接 (由于socket存在假死状态不推荐使用readyState)
    11. this.connectStatus = -1;
    12. // 重连定时器
    13. this.reConnInterval = null;
    14. // 重连次数
    15. this.RECONNECTION_NUMBER = 20;
    16. // 记录重连次数
    17. this.reconnectionCount = 0;
    18. // 是否开启定心跳检测
    19. this.isHeartCheck = false;
    20. // 心跳检测 定时器 初始化
    21. this.heartTimeout = null;
    22. this.heartServerTimeout = null;
    23. // 链接websocket
    24. this.initConnectSocket();
    25. // 访客进入
    26. this.visitor = options.visitor;
    27. // 消息推送
    28. this.pushMsg = options.pushMsg;
    29. // 删除消息
    30. this.removeChatList = options.removeChatList;
    31. // 连接/重连 拉取最新消息(可socket返回或ajax请求拉取数据)
    32. this.reConnectGetMsg = options.reConnectGetMsg;
    33. }
    34. // 获取socket链接地址
    35. getWSURL() {
    36. return "ws://121.40.165.18:8800";
    37. }
    38. // 初始化 连接websocket
    39. initConnectSocket() {
    40. if (!Reflect.has(window, "WebSocket")) {
    41. return console.log("您的浏览器不支持WebSocket");
    42. }
    43. // 清除重连定时器
    44. this.reConnInterval && clearInterval(this.reConnInterval);
    45. // 判断socket状态
    46. if (this.ws && this.ws.readyState === 1) {
    47. this.ws.close();
    48. this.wss = null;
    49. }
    50. this.connectStatus = 0;
    51. this.ws = new WebSocket(this.getWsURL());
    52. // WebSocket 连接成功后的回调函数
    53. this.ws.addEventListener(
    54. "open",
    55. () => {
    56. console.log("websocket connection successful");
    57. this.connectStatus = 1;
    58. // 连接次数归零
    59. this.reconnectionCount = 0;
    60. //处理未发送或者发送失败消息
    61. this.socketMsgQueue.forEach((item) => {
    62. // 重新发送失败的消息
    63. this.socketSendMessage(item);
    64. });
    65. this.socketMsgQueue = [];
    66. this.reConnectGetMsg();
    67. // 判断是否启动 socket 心跳检测
    68. if (this.isHeartCheck) {
    69. this.heartCheck();
    70. }
    71. },
    72. false
    73. );
    74. // 监听服务器接受到信息时的回调函数
    75. this.ws.addEventListener(
    76. "message",
    77. (res) => {
    78. let message = {};
    79. if (res.data && typeof res.data !== "object") {
    80. message = JSON.parse(res.data);
    81. }
    82. // 批量处理
    83. if (Array.isArray(message)) {
    84. message.forEach((item) => {
    85. this.processMessage(item);
    86. });
    87. } else {
    88. this.processMessage(message);
    89. }
    90. },
    91. false
    92. );
    93. // WebSocket 连接关闭后的回调函数
    94. this.ws.addEventListener(
    95. "close",
    96. (error) => {
    97. console.log(`socket has been closed, ${error}`);
    98. this.connectStatus = -1;
    99. this.reLinkSocket();
    100. },
    101. false
    102. );
    103. // WebSocket 连接失败后的回调函数
    104. this.ws.addEventListener(
    105. "error",
    106. (error) => {
    107. console.log(`socket has been error, ${error}`);
    108. this.connectStatus = -1;
    109. this.reLinkSocket();
    110. },
    111. false
    112. );
    113. }
    114. // 监听信息推送
    115. processMessage(message) {
    116. // 接受到心跳检测返回值
    117. if (message === "pong") {
    118. return this.heartCheck();
    119. }
    120. // 访客进入
    121. if (message.types === "访客进入") {
    122. this.visitor(message);
    123. }
    124. console.log(message);
    125. }
    126. // 发送信息
    127. async sendMessage(data) {
    128. console.log(`发送消息给服务器:${data}`);
    129. // 发送消息时,先添加到消息列表显示出来
    130. // TODO:不能等服务器返回在显示,由于消息服务器可能存在积压状态导致消息推送过慢情况处理
    131. this.pushMsg(premsg);
    132. if (this.connectStatus === 1) {
    133. return this.ws.send(data);
    134. }
    135. this.socketMsgQueue.push(message);
    136. await this.closeSocket();
    137. await this.reLinkSocket();
    138. }
    139. // 撤回消息
    140. withdrawMessage(data) {
    141. this.sendMessage(data);
    142. // 删除消息
    143. this.removeChatList(data);
    144. }
    145. /**
    146. * @method 接收消息后格式化消息
    147. * @param {Object} message 消息内容
    148. * @param {Boolean} isOwn 是否属于自己发出的消息
    149. * @return {Array} 返回格式化后消息
    150. */
    151. formatMessageTalk(message, isOwn) {
    152. // some code...
    153. }
    154. // 心跳包检测
    155. heartCheck() {
    156. // 心跳时间与收发内容以后端约定为主
    157. this.heartTimeout && clearTimeout(this.heartTimeout);
    158. this.heartServerTimeout && clearTimeout(this.heartServerTimeout);
    159. this.heartTimeout = setTimeout(() => {
    160. // 检查socket链接状态 才可发送
    161. if (this.connectStatus === 1) {
    162. this.ws.send("ping");
    163. }
    164. // 如果5s内没有返回约定值判定socket处于假死状态,重新链接socket
    165. this.heartServerTimeout = setTimeout(async () => {
    166. console.log("heartCheck timeout");
    167. await this.closeSocket();
    168. await this.reLinkSocket();
    169. }, 5000);
    170. }, 20000);
    171. }
    172. // 关闭WebSocket
    173. closeSocket() {
    174. if (this.ws) {
    175. this.ws.close();
    176. this.ws = null;
    177. // 清除重连定时器
    178. this.reConnInterval && clearInterval(this.reConnInterval);
    179. // 清除心跳包定时器
    180. this.heartTimeout && clearTimeout(this.heartTimeout);
    181. this.heartServerTimeout && clearTimeout(this.heartServerTimeout);
    182. }
    183. }
    184. // 断线重连
    185. // TODO: 严谨写法需要判断主动重连与被动重连情况
    186. reLinkSocket() {
    187. console.log("reconnection webscoket function");
    188. // 清除心跳包定时器
    189. this.heartTimeout && clearTimeout(this.heartTimeout);
    190. this.heartServerTimeout && clearTimeout(this.heartServerTimeout);
    191. // 清除重连定时器
    192. this.reConnInterval && clearInterval(this.reConnInterval);
    193. this.reConnInterval = setInterval(() => {
    194. // 判断是否达到重连次数
    195. if (this.reconnectionCount < RECONNECTION_NUMBER) {
    196. this.connectSocket();
    197. this.reconnectionCount += 1; // 重连次数
    198. } else {
    199. clearInterval(this.reConnInterval);
    200. }
    201. }, 5000);
    202. }
    203. }
    204. // use case
    205. const websocket = new WebSocketClass({
    206. // 访客进入
    207. visitor: (userInfo) => {
    208. console.log(userInfo);
    209. // some code...
    210. },
    211. // 消息推送
    212. pushMsg: (msgdata) => {
    213. console.log(msgdata);
    214. // some code...
    215. },
    216. // 删除消息
    217. removeChatList: (messageId) => {
    218. console.log(messageId);
    219. // some code...
    220. },
    221. // 连接/重连 拉取最新数据
    222. reConnectGetMsg: () => {
    223. // 拉取最新消息
    224. },
    225. });