WebSocket & socket.io

220 阅读5分钟

一、WebSocket介绍

  1. WebSocket协议是基于TCP的一种新的网络协议,它实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动发送信息给客户端。
  2. WebSocket是一种持久性协议,http是非持久性协议。

现在很多网站都有实时推送的需求,比如聊天,客服咨询等

早期没有WebSocket时,由于http请求服务器无法主动给浏览器发送数据,所以通过ajax轮询,浏览器定时给服务器发送请求(比如1s一次),服务器把最新的数据响应给浏览器。这种模式的缺点就是浪费性能和资源。 websocket.png 由图可知,http协议每发送一次请求,服务器给一次响应,然后就断开,直到下一次客户端发起请求;WebSocket在客户端建立好连接后,客户端和服务端之间不用断开,可以互相主动地发送消息。

WebSocket是一种网络协议,允许客户端和服务端全双工的进行网络通信,服务端可以给客户端发送消息,客户端也可以给服务端发送消息。

二、使用node创建WebSocket服务

1、项目搭建

  1. mkdir server
  2. cd server
  3. npm init -y
  4. npm i nodejs-websocket

2、server下新建app.js

const ws = require('nodejs-websocket') // 导入nodejs-websocket
const POST = 3000

// 创建socket服务
const server = ws.createServer(conn => {
  console.log('接收到了新的连接') // 当浏览器新的窗口连接到ws://localhost:3000会触发这里

  // 接收到客户端的文本内容时触发
  conn.on('text', data => {
    conn.send('我是服务器返回的:' + data.toUpperCase()) // 发送至客户端
  })

  // 关闭事件,浏览器窗口关闭时触发
  conn.on('close', () => {
    console.log('关闭了连接')
  })

  // 异常事件:浏览器窗口关闭了,发送的数据格式不对...
  conn.on('error', () => {
    console.log('连接异常')
  })
})

server.listen(POST, () => {
  console.log('服务器启动成功,端口号为:' + POST)
})

3、终端中执行node app.js

image.png 此时,基于node的socket服务就搭建好了,占用的端口号为3000

三、WebSocket基本使用

在H5中,浏览器已经实现了WebSocket的API,可以直接使用

WebSocket-MDN

  1. 创建WebSocket实例
      const socket = new WebSocket('ws://localhost:3000')
  1. WebSocket事件
事件事件处理程序触发时机
opensocket.onopen连接建立时触发
messagesocket.onmessage客户端接收到服务端数据时触发
errorsocket.onerror通信发生错误时触发
closesocket.onclose连接关闭时触发
  1. WebSocket方法
方法描述
send发送数据至服务端
close客户端手动关闭连接
  <body>
    <!-- 输入内容 -->
    <input type="text" placeholder="输入你的内容" />
    <!-- 发送请求 -->
    <button>发送请求</button>
    <!-- 显示结果 -->
    <div></div>

    <script>
      const input = document.querySelector('input')
      const button = document.querySelector('button')
      const div = document.querySelector('div')

      const socket = new WebSocket('ws://localhost:3000') // 创建socket实例

      // open事件:当和WebSocket服务连接成功时触发
      socket.addEventListener('open', function () {
        div.innerHTML = '连接成功了'
      })

      // 主动给WebSocket服务发送消息
      button.addEventListener('click', function () {
        const { value } = input
        console.log(value)
        socket.send(value)
      })

      // 接收WebSocket服务器的数据
      socket.addEventListener('message', function (e) {
        console.log(e)
        div.innerHTML = e.data
      })

      socket.addEventListener('close', function () {
        div.innerHTML = '服务器断开连接'
      })
    </script>
  </body>

效果: 动图.gif

四、基于以上的知识点,做一个聊天室DEMO

1、效果

动图.gif 可以看到这个dome中,某个用户进来/离开,发送消息时,服务器都会实时推送给所有的用户。这正体现了WebSocket是持久性的协议,并且会主动发送消息至客户端。这一点要是用http请求就完蛋了,每次由客户端发起请求再得到相应,这期间得产生多少资源的浪费

2、app.js

const ws = require('nodejs-websocket') // 导入nodejs-websocket
const POST = 3000

let userCount = 0
const nameMap = new Map([
  [1, '王总'],
  [2, '1号技师'],
  [3, '2号技师']
])

const TYPE_ENTER = 0
const TYPE_LEAVE = 1
const TYPE_MSG = 2

// 通过广播给所有的用户发送消息 connections是一个数组,里面是所有的用户;conn.send只是给当前用户发送消息
const broadcase = data => {
  for (const item of server.connections) item.send(JSON.stringify(data))
}

// 每个连接到服务器的用户,都有个conn对象
const server = ws.createServer(conn => {
  console.log('接收到了新的连接')
  userCount++
  conn.username = nameMap.get(userCount)

  // 1、当有用户连接上服务器时,广播告诉所有的用户,谁进入了,什么时间进入的
  broadcase({
    type: TYPE_ENTER,
    msg: `${conn.username}进入了聊天室`,
    time: new Date().toLocaleTimeString()
  })

  conn.on('text', data => {
    // 当有一个用户发送消息时,广播告诉所有的用户,谁发送了xx消息,什么时间发送的
    broadcase({
      type: TYPE_MSG,
      msg: data,
      time: new Date().toLocaleTimeString()
    })
  })

  conn.on('close', () => {
    userCount--
    console.log('关闭了连接')
    broadcase({
      type: TYPE_LEAVE,
      msg: `${conn.username}离开了聊天室`,
      time: new Date().toLocaleTimeString()
    })
  })

  conn.on('error', () => {
    console.log('连接异常')
  })
})

server.listen(POST, () => {
  console.log('服务器启动成功,端口号为:' + POST)
})

3、user.html

  <body>
    <!-- 输入内容 -->
    <input type="text" placeholder="输入你的内容" />
    <!-- 发送请求 -->
    <button id="send">发送请求</button>
    <!-- 显示结果 -->
    <ul></ul>

    <script>
      const input = document.querySelector('input')
      const button = document.querySelector('#send')
      const ul = document.querySelector('ul')
      const colorMap = new Map([
        [0, 'yellowgreen'],
        [1, 'red'],
        [2, 'blue']
      ])
      const nameMap = new Map([
        [1, '王总'],
        [2, '1号技师'],
        [3, '2号技师']
      ])

      const socket = new WebSocket('ws://localhost:3000') // 创建socket实例

      // open事件:当和WebSocket服务连接成功时触发
      socket.addEventListener('open', function () {
        ul.innerHTML = '已连接服务器'
      })

      // 主动给WebSocket服务发送消息
      button.addEventListener('click', function () {
        const { value } = input
        socket.send(value)
        input.value = ''
      })

      // 接收WebSocket服务器的数据
      socket.addEventListener('message', function (e) {
        const { type, msg, time, userCount } = JSON.parse(e.data)
        const li = document.createElement('li')
        li.style.color = colorMap.get(type)
        li.innerText = `${msg}----${time}`
        ul.appendChild(li)
      })

      socket.addEventListener('close', function () {
        div.innerHTML = '服务器断开连接'
      })
    </script>
  </body>

五、socket.io基本使用

在socket.io中,通过emit方法触发自定义事件,通过on方法接收自定义事件

1、创建一个文件夹,安装socket.io

yarn add socket.io

app.js

const http = require('http')
const fs = require('fs')
const app = http.createServer()

// 当访问localhost:3000时,返回index.html
app.on('request', (req, res) => {
  fs.readFile(__dirname + '/index.html', function (err, data) {
    if (err) {
      res.writeHead(500)
      return res.end('Error loading index.html')
    }
    res.writeHead(200)
    res.end(data)
  })
})

app.listen(3000, () => {
  console.log('服务器启动成功')
})

// 以上代码创建了一个node服务
const io = require('socket.io')(app)
// 监听用户连接事件
io.on('connection', socket => {
  console.log('有新用户连接了')
  socket.on('hehe', data => {
    console.log('服务器接收', data)
    socket.emit('haha', data)
  })
})

这里也可以使用express启动服务:

  1. yarn add express
  2. app.js
const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server)

server.listen(3000, () => {
  console.log('服务器启动成功')
})

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html')
})

io.on('connection', socket => {
  socket.on('hehe', data => {
    console.log('服务器接收', data)
    socket.emit('haha', data)
  })
})

3、index.html

  <body>
    123
    <script src="/socket.io/socket.io.js"></script>
    <script>
      console.log(io)
      const socket = io('localhost:3000')

      socket.on('haha', data => {
        console.log('浏览器接收', data)
      })

      socket.emit('hehe', { name: 'xx', age: 10 })
    </script>
  </body>

4、效果:浏览器打开http://localhost:3000/

动图.gif