浏览器websocket技术知识点记录

135 阅读6分钟

websocket是一种在单个tcp连接上进行双工通信的协议,能提供比传统的HTTP连接更加高效,低延迟的实时通信

websocket

基本介绍

websocket主要有如下特点:

1. 双工通信

在客户端发起与服务器端建立连接之后,websocket允许客户端和服务器端进行双工通信,在连接的情况下,任何一方在任何时候都可以主动的发送消息。

2. 持久连接

websocekt是持久连接的,一旦建立连接,它将保持打开状态,允许任何时候发送消息,而不需要重新建立连接

3. 低延迟

由于webscoket的全双工通信,它比传统的http请求-响应模型具有更低的延迟,不需要每次都建立连接,所以它非常适合实时通信的应用,如聊天和游戏。

4. 简化的协议

websocket的协议相对简单,基于帧结构,与传统的HTTP相比,它没有请求头和响应头,减少了通信的开销

5. 跨域通信

websocket是协议标识符是ws(未加密)和wss(加密),它不受浏览器同源策略的影响,可以在不同域名之间进行通信,不需要处理跨域问题。

连接过程

websocket虽然是基于ws协议,但是创建连接的过程与http类似,也是需要建立一个tcp连接,在这个过程中会告知服务器升级为ws协议,如果服务器支持ws服务,则返回101状态码告诉浏览器已经切换为ws协议

1. 握手阶段

客户端发起连接

客户端通过向服务器端发送一个http请求来发起websocket连接,这个请求包括两个特殊的头部connection: upgradeupgrade:websocket,即升级为websocket协议

服务器响应握手

服务器端收到请求后,如果支持websocket,会返回一个101状态码,表示切换协议

建立连接

握手成功后,就正式建立了连接,此时客户端和服务器端可以通过这个连接进行双工通信

2. 通信阶段

数据帧传输

建立连接后,双端是通过发送数据帧进行通信的,websocket数据帧是协议定义的基本数据单元

帧格式

每个数据帧可以包含文本文本,二进制数据或控制帧,帧的格式包括一个帧头和帧体

3. 保持连接

心跳检测

为了保持连接的活跃性,客户端和服务端通常会定期发送心跳(ping)消息,如果一方在定义的时间内没有收到对方的心跳响应,就可以认为连接已经断开

重连机制

重连机制建立在心跳检查的基础上,如果心跳检查确定断开连接,则发起websockt的重新连接,一般会重复进行尝试,等尝试都失败后,就可以认为服务器已经死掉,无法建立连接。

4. 关闭连接

关闭握手

当任一方决定关闭连接时,它可以发送一个关闭握手帧来表示希望关闭连接,对方收到关闭帧后,也会发送一个关闭帧作为确认

连接关闭

一旦双方都发送了关闭帧,则连接就会正式关闭。

基本使用

websocket是浏览器支持的一个模块,可以在JavaScript中直接使用

// 通过拼接url的方式传递参数
const socket = new Websocket('ws://xxxxx?userId=xxx')
// 建立连接
socket.onopen = () => {
    console.log('连接建立...')
    socket.send('发送消息')
}
// 监听消息
socket.onmessage = (event) => {
    console.log(event.data)
    socket.close() // 关闭连接
}
// 连接关闭
socket.onclose = () => {
}
// 连接错误
socket.onerror = () => {
}

心跳检测与重连

心跳检测主要用在客户端,用来判断与服务器的连接是否保持畅通,常见的场景是客户端网络突然断开或服务器端重启,这都会造成websocket连接中断。

实现方案:客户端定时向服务器的发起一个心跳消息,服务器收到消息后向客户端发送一个心跳,客户端监听心跳消息,如果能监听到则连接正常,否则就出现异常,进行重新。

let heart = false
const reConnect =() => {}
// 连接成功设置心跳为true
socket.on('connect', () => {
    heart = true
    // 开始心跳检测
    setInterval(() => {
        if (heart) {
            heart = false
            socket.emit('heart')
        } else {
           // 如果为false则心跳检测失败,需要重连
           reConnect()
        }
    }, 3000)
    
   // 监听服务端的心跳
    socket.on('heart', () => {
        heart = true
    })
})
// 监听到客户端的心跳后,马上给客户端回复一个消息
socket.on('heart', () => {
    socket.emit('heart')
})

socket.io

虽然浏览器提供的websocket api完全可以用来实现实时通信的功能,但是很多的异常场景都需要自己去实现,难免会有很多地方考虑的不周全,目前用到最多的就是socket.io, 支持客户端和服务器端。

socket.io服务端

创建连接监听

import http from 'http';
import express from 'express';
import { Server } from 'socket.io'
const app = express();
const server = http.createServer(app);

app.use(express.static('public'))
const io = new Server(server, {
    cors: {
        origin: '*'
    }
});

io.on('connection', (socket) => {
     // 获取参数
     const { userId, userName } = socket.handshake.query;
     socket.broadcast.emit('message') // 广播消息
     socekt.emit('message') // 发送消息
     console.log('连接成功')
    // 监听客户端发送的消息
    socket.on('message', (data) => {
    })
    // 监听用户断开连接
    socket.on('disconnect', () => {
        console.log(userId + '断开连接')
    });
});

server.listen(8000, () => {
    console.log("server is up and running on port 8000");
});

socket.io客户端

创建连接监听

 import { io } from 'socket.io-client'
 const socket = io('http://192.168.x.x:8000', {
    query: {
        userId: xxx
    }
})
 socket.on('connection', ()=> {
    console.log('连接成功')
})
socket.on('message', (data)=> {
    console.log('监听到消息')
    socket.emit('发送消息...')
})
socket.on('disconnect', ()=> {
    console.log('断开连接')
})

两者对比

  1. websocket是html5里面的一个标准的协议,主要用来实现客户端与浏览器端数据的双向通信。

  2. socket.io是一个封装了websocket,长轮询等传输方式的库,它的目标是提供统一的接口,使得在不同环境(不管支持或不支持websocket的i情况下)都能实现实时的双向通信。

  3. socket.io创建的服务不是标准的websocket服务,尽管socket.io可以使用websocket作为其中的一种传输方式,但socket.io本身并不仅仅限于websocket协议,它是一个独立的实现,具有自己的通信协议和机制。

  4. 一般情况下webscoket客户端和socket.io服务器之间不能直接连接,反之socket.io客户端和websocket服务器端也不能直接连接。

  5. websocket建立连接时通过http握手升级到websocket协议,socket.io首先默认发起一个普通的http请求(路径会携带/socket.io), 这个请求告知socket.io服务器希望升级为websocket服务,如果支持websocket则升级,如果不支持则降低到其他方式(长轮询),所以创建socket.io连接时会存在跨域问题。