webrtc学习记录二【基于socket.io创建信令服务器聊天室】

系列文章目录

webrtc学习记录一【媒体录制MediaRecorder】

webrtc学习记录三【创建基于RTCPeerConnection本机内的1v1音视频互通】


目录

系列文章目录

前言

一、nodejs信令服务器

1.1、基本服务器代码

1.2、绑定socket.io,创建信令服务器。

1.3、完整服务器代码

关键代码

完整代码

运行效果


前言

记录webrtc学习过程中的要点,以便温故知新。本章主要基于socket.io创建一个信令服务器,并且实现一个简单的聊天室功能。

同时,由于学习视频中的socket.io的版本比较低,和最新版的有区别,所以学习过程中也踩了一些坑,用新版本实现了功能并且处理了一些问题,如跨域等。


提示:以下是本篇文章正文内容,下面案例可供参考

一、nodejs信令服务器

首先使用nodejs和express实现一个简单的服务器,由于这里我使用的是https,所以额外配置了一些证书相关的代码,但是并不影响功能。

1.1、基本服务器代码

'use strict'
const https = require('https')
const fs = require('fs')
const express = require('express')
const serveIndex = require('serve-index')

const app = express()

// 浏览指定目录,加上这个之后就可以浏览静态资源目录。
app.use(serveIndex('./public'))
// 静态资源发布到指定目录
app.use(express.static('./public'))

const options = {
	key: fs.readFileSync('./cert/tongyichen.com.key'),
	cert: fs.readFileSync('./cert/tongyichen.com.pem'),
}
const https_server = https.createServer(options, app, (req, res) => {
  console.log('req', req)
})

https_server.listen(443, '0.0.0.0', (_) => {
	console.log(`https服务器创建成功!启动时间:${new Date().toLocaleString()}`)
	console.log('访问地址:')
	console.log('https://106.55.160.183:443')
	console.log('https://tongyichen.com')
})

1.2、绑定socket.io,创建信令服务器。

首先引入socket.io,

const socketIo = require('socket.io')

然后将socket.io和https_server服务器绑定。

此处需要注意的是,设置cors:true,即可允许跨域。

学习视频中的代码,由于服务器代码和客户端代码是同一地址,所以不存在跨域。但是实际生产开发中,前后端分离,服务器与客户端分离的跨域情况很常见。

查阅了相关的资料和文档,里面介绍的什么设置origin,更换端口,都无效。

最后是输出了io信息,发现io信息中有一个属性cors默认是false,于是便想到修改cors为true,结果正好解决了跨域问题。

虽然解决代码很简单,但是解决过程很艰辛。

const io = socketIo(https_server, {
  cors: true // 中文文档没更新,看代码找到的设置cors为true即可解决跨域问题,nice
})

socket.io绑定到服务器之后,就可以开始处理建立连接后的逻辑了。

这里监听connection连接事件,然后在建立连接后,继续监听join,leave,message事件。

需要说明的一点是,不知道是否因为版本原因,使用视频中的io.sockets.adapter.rooms[roomId]无法找到,输出打印信息后发现,4.1.2版本的socket.io中的io.sockets.adapter.rooms为一个map格式的数据,并且返回的是一个set格式的数据。所以使用原方法自然获取到的是undefined,按set数据的size属性即可获取到当前房间的人数。

代码内容也不难理解,join成功后,就触发一个joined事件,同时抛出相关数据给客户端。leave成功后,就抛出left事件给客户端。监听到message事件时,就给房间内所有人都发送消息。

这里倒是可以参看socket.io文档中的速查表,来查看发送消息的方法:

引自文档:

io.on('connect', onConnect);

function onConnect(socket){

  // 发送给当前客户端
  socket.emit('hello', 'can you hear me?', 1, 2, 'abc');

  // 发送给所有客户端,除了发送者
  socket.broadcast.emit('broadcast', 'hello friends!');

  // 发送给同在 'game' 房间的所有客户端,除了发送者
  socket.to('game').emit('nice game', "let's play a game");

  // 发送给同在 'game1' 或 'game2' 房间的所有客户端,除了发送者
  socket.to('game1').to('game2').emit('nice game', "let's play a game (too)");

  // 发送给同在 'game' 房间的所有客户端,包括发送者
  io.in('game').emit('big-announcement', 'the game will start soon');

  // 发送给同在 'myNamespace' 命名空间下的所有客户端,包括发送者
  io.of('myNamespace').emit('bigger-announcement', 'the tournament will start soon');

  // 发送给指定 socketid 的客户端(私密消息)
  socket.to(<socketid>).emit('hey', 'I just met you');

  // 包含回执的消息
  socket.emit('question', 'do you think so?', function (answer) {});

  // 不压缩,直接发送
  socket.compress(false).emit('uncompressed', "that's rough");

  // 如果客户端还不能接收消息,那么消息可能丢失
  socket.volatile.emit('maybe', 'do you really need it?');

  // 发送给当前 node 实例下的所有客户端(在使用多个 node 实例的情况下)
  io.local.emit('hi', 'my lovely babies');

};

1.3、完整服务器代码

最后,完整的nodejs信令服务器端代码如下:

'use strict'
// const http = require('http')
const https = require('https')
const fs = require('fs')
const express = require('express')
const serveIndex = require('serve-index')
const socketIo = require('socket.io')

const app = express()

// 浏览指定目录,加上这个之后就可以浏览静态资源目录。
app.use(serveIndex('./public'))
// 静态资源发布到指定目录
app.use(express.static('./public'))

const options = {
	key: fs.readFileSync('./cert/tongyichen.com.key'),
	cert: fs.readFileSync('./cert/tongyichen.com.pem'),
}
const https_server = https.createServer(options, app, (req, res) => {
  console.log('req', req)
})
// bind socket.io with https_server, options 参考https://www.w3cschool.cn/socket/socket-odxe2egl.html,此文档并不齐全。
const io = socketIo(https_server, {
  cors: true // 中文文档没更新,看代码找到的设置cors为true即可解决跨域问题,nice
})

//io.sockets 站点, socket,当前客户端。
io.sockets.on('connection', (socket) => {
	// socket代表每一个客户端
	socket.on('join', (roomId) => {
		console.log('join success, roomId is ', roomId)
		socket.join(roomId)
		// const myRoom = io.sockets.adapter.rooms[roomId]
    const userCount = io.sockets.adapter.rooms.get(roomId).size
		console.log(`the number of user is : ${userCount}`)
		// socket.emit('joined', roomId, socket.id) // 给该客户端单独返回消息。
		// socket.to(roomId).emit('joined', roomId, socket.id) // 给房间内,除了自己以外的所有人返回消息。
		io.in(roomId).emit('joined', {
			// 给房间内的所有人都发送消息。
			userCount,
			roomId,
			id: socket.id,
		})
		// socket.broadcast.emit('joined', roomId, socket.id) // 给出了自己,全部站点的所有人发送消息。broadcast 广播
	})

	socket.on('leave', (roomId) => {
		console.log('join success, roomId is ', roomId)
		socket.leave(roomId)
		// const myRoom = io.sockets.adapter.rooms[roomId]
    const userCount = io.sockets.adapter.rooms.get(roomId).size
		console.log(`the number of user is : ${userCount}`)
		// socket.emit('joined', roomId, socket.id) // 给该客户端单独返回消息。
		// socket.to(roomId).emit('joined', roomId, socket.id) // 给房间内,除了自己以外的所有人返回消息。
		io.in(roomId).emit('left', {
			// 给房间内的所有人都发送消息。
			userCount,
			roomId,
			id: socket.id,
		}) // 给房间内的所有人都发送消息。
		// socket.broadcast.emit('left', roomId, socket.id) // 给出了自己,全部站点的所有人发送消息。broadcast 广播
	})

	socket.on('message', (roomId, data) => {
    console.log('收到消息', data)
		// socket.to(roomId).emit('message', {
		io.in(roomId).emit('message', {
			// 给房间内的所有人都发送消息。
			roomId,
			id: socket.id,
			data,
		})
	})
})

https_server.listen(443, '0.0.0.0', (_) => {
	console.log(`https服务器创建成功!启动时间:${new Date().toLocaleString()}`)
	console.log('访问地址:')
	console.log('https://106.55.160.183:443')
	console.log('https://tongyichen.com')
})


二、客户端代码实现

先搭建doom框架,这里我选用的vue简单开发了一下界面。

简单的需求逻辑为:

用户a输入房间号码111,加入该房间。

用户b输入房间号码111,加入该房间

然后用户a发送的消息,a和b均能收到。用户b发送的消息,a和b也能收到。

梳理一下开发流程,应该是:

1、连接socket.io服务器,

2、监听服务器返回的joined事件(因为在上面服务器代码中有监听join事件,然后触发一个joined事件)

3、连接成功建立后,即可通过触发message事件到服务器端,而服务器端的socket监听到了message事件后,同样触发一个message事件到客户端,客户端监听到服务器端返回的message事件和数据后,呈现数据即可。

语言描述比较绕,用流程图画一下就显得很清楚:

关键代码

    sendMessage() {
      let data = `${this.userName}:${this.inputData}`
      console.log('发送的消息为', data)
      this.socket.emit('message', this.roomId, data)
      this.inputData = ''
    },
    joinRoom() {
      // 建立链接 connect中为链接的socket.io服务器的地址
      // this.socket = io.connect('https://192.168.1.153')
      this.socket = io.connect('https://tongyichen.com')

      this.socket.on('joined', ({userCount, roomId, id}) => {
        this.joinStatus = true
      })
      this.socket.on('left', ({userCount, roomId, id}) => {
        this.joinStatus = false
      })
      this.socket.on('message', ({userCount, roomId, id, data}) => {
        this.chatData = this.chatData + data + '\n'
      })

      console.log('roomId', this.roomId)
      this.socket.emit('join', this.roomId)
    },

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>

  </style>
</head>
<body>
  <div id="app">
    <p style="text-align: center;">聊天室</p>
    <div class="line">
      <div class="label">用户名:</div>
      <input v-model="userName" placeholder="请输入用户名">
    </div>
    <div class="line">
      <div class="label">房间号:</div>
      <input v-model="roomId" placeholder="请输入房间号">
      <button @click="joinRoom" :disabled="joinStatus">加入房间</button>
      <button @click="leaveRoom" :disabled="!joinStatus">离开房间</button>
    </div>
    <div>
      <div>聊天框:</div>
      <textarea style="width:500px;height:200px" v-html="chatData"></textarea>
    </div>
    <hr>
    <div>
      <div>输入框:</div>
      <textarea style="width:500px;height:70px" v-model="inputData"></textarea>
      <div> <button @click="sendMessage">发送消息</button> </div>
    </div>
  </div>
</body>
<script src="./js/socket.io.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
var app = new Vue({
  el: '#app',
  data: {
    joinStatus: false,
    message: 'Hello Vue!',
    userName: '',
    roomId: '',
    chatData: '',
    inputData: '',
    socket: null
  },
  methods: {
    sendMessage() {
      let data = `${this.userName}:${this.inputData}`
      console.log('发送的消息为', data)
      this.socket.emit('message', this.roomId, data)
      this.inputData = ''
    },
    joinRoom() {
      // 建立链接 connect中为链接的socket.io服务器的地址
      // this.socket = io.connect('https://192.168.1.153')
      this.socket = io.connect('https://tongyichen.com')

      this.socket.on('joined', ({userCount, roomId, id}) => {
        this.joinStatus = true
      })
      this.socket.on('left', ({userCount, roomId, id}) => {
        this.joinStatus = false
      })
      this.socket.on('message', ({userCount, roomId, id, data}) => {
        this.chatData = this.chatData + data + '\n'
      })

      console.log('roomId', this.roomId)
      this.socket.emit('join', this.roomId)
    },
    leaveRoom() {
    }
  },
  created() {
    console.log('io', io)
  }
})
</script>
</html>


运行效果

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值