WebSocket的介绍与使用,带你彻底搞懂WebSocket

WebSocket的介绍与使用,带你彻底搞懂WebSocket

背景

在没有WebSocket时,我们如果想不断的拿到服务器的消息,比如要求实时性非常高的场景:股票、在线聊天、通知等。我们需要使用短轮询长轮询的方式实现。

短轮询

浏览器每隔一小段时间,去请求服务器询问是否有新消息,实现很简单,使用定时器 setTimeout 即可。
这样会导致哪些不足呢?

  • 因为需要不断的去请求,定义时间短了就会导致发送了很多非必要的请求,时间长了实时性又不高。
  • 频繁的建立关闭连接

长轮询

长轮询是为了解决短轮询频繁请求的问题。他的表现为客户端发送了请求,服务器没有新消息就等待直到有了新消息才发送给客户端。
长轮询也有不足:

  • 只有服务器有了新消息才会发给浏览器,这就会导致长时间没有响应给浏览器导致超时,浏览器就会主动断开本次连接。当然,我们可以在超时后重新发送请求,但这样会导致之前超时的请求都是无意义的。
  • 在连接的过程中,如果服务器没有发送消息,就会一直占用着资源,但什么也没有做。

为什么使用WebSocket

无论短轮询还是长轮询,都是客户端主动发送请求给服务器实现通信,他们都是建立在HTTP 协议之上,所以不支持服务器主动给浏览器推送消息。而 websocket 具有全双工通信(服务器和客户端可同时发送和接收消息)的能力,与 HTTP 都是基于 TCP 的应用层协议。
以下是 WebSocket 需要注意的点,面试中可能会问到:

  • WebSocket 是在 HTML5 新实现的功能,对古老版本的浏览器并不支持
  • 对于消息少的,即不会频繁响应消息的场景,不必使用 WebSocket,不然会一直维持着 TCP 的连接,造成资源白白浪费。
  • WebSocket 的使用中会经历两个阶段:握手和通信
    • 握手阶段
      需要注意的是使用 HTTP 协议发送请求而完成的 WebSocket握手
      • websocket相关的请求头如下:
        Sec-Websocket-Key: DBY94ycLOm0InY0I+OgGYg== // base64编码,要求服务端有对应加密的编码返回
        Connection: Upgrade // 通知服务端升级
        Upgrade: websocket // 升级为websocket
        Sec-Websocket-Version: 13 // websocket的版本
      • websocket相关的响应头如下:
        Connection: Upgrade
        Sec-Websocket-Accept: D8gT50cLdyuUbQzFvG6BWUhJEJw= // 与客户端相同密钥计算后的密文
        Upgrade: websocket
      • 响应行中包含 switching protocals 表示握手成功,可以开始TCP通信了在这里插入图片描述
    • 通信阶段
      握手结束后,不再使用HTTP,双方可任意给对方发送消息,直到双方主动断开或者因为网络等原因被迫断开,可以通过心跳检测机制实现断线重连。
  • 基于 HTTP 的短轮询或长轮询,都需要先请求后响应,服务器无法主动响应消息给浏览器,而 WebSocket 可以在使用 HTTP 完成握手之后与服务器建立持久连接,实现双方即时通信。

WebSocket 的简单使用

  • node 后端部分
const { WebSocketServer } = require('ws');
function createWebSocketServer(port) {
  const ws = new WebSocketServer({ port }, () => {
    console.log(`WebSocket server is listening on port ${port}`);
  });
  // 建立连接
  ws.on('connection', (socket) => {
    // 收到客户端的消息
    socket.on('message', (message) => {
      socket.send(message.toString()); // 回显消息
    })
  })
}
createWebSocketServer(3000);
  • 前端部分,这里使用 vue 做了一个简单实现
<template>
  <div class="button-groups">
    <button @click="connect">建立连接</button>
    <button @click="closeConnect">关闭连接</button>
  </div>
  <div class="input-groups">
    <input type="text" placeholder="请输入内容" @keydown.enter="send" v-model="content" />
    <button @click="send">发送</button>
  </div>
  <ul class="container">
    <li class="item" v-for="item in items" :key="item.id">{{ item.content }}</li>
  </ul>
</template>

<script setup lang='ts'>
import { ref } from 'vue';
interface IItems {
  id: number,
  content: string
}
const items = ref<IItems[]>([]);
const socket = ref<WebSocket | null>(null);
const content = ref('');
const connect = () => {
  if (socket.value) {
    return;
  }
  socket.value = new WebSocket('ws://localhost:3000');
  socket.value.onopen = () => {
    console.log('已建立连接');
  }
  socket.value.onmessage = (event) => {
    items.value.push({
      id: items.value.length,
      content: e.data // event.data 是服务器发送的数据
    });
  }
  socket.value.onclose = () => {
    console.log('连接已关闭')
	  }
	}
  const send = () => {
	 if (!content.value) {
	   return;
	 }
	 const val = content.value.trim();
	 socket.value?.send(val);
	 content.value = '';
  }
  const closeConnect = () => {
    if (!socket.value) {
	  return;
	}
	  socket.value.close();
	}
</script>

从上面代码可以看出目前的通信是点对点,只能通知单个客户端,如果想通知所有客户端该如何实现呢?
很简单,我们只需利用广播模式将之前监听消息的代码改为:

socket.on('message', (message) => {
  ws.clients.forEach((client) => {
    client.send(message.toString()); // 回显消息
  })
})

https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/WebSocket mdn参考文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值