WebRTC(六) Node.js+Socket.io实现信令服务器

1 信令服务器的作用

WebRTC需要一个房间服务器将多端聚集到一起管理,以及信令服务器进行信令数据交换(如媒体描述信息SDP的交换,连接地址的交换等),但在WebRTC的规范中没有对这部分内容进行规定,所以需要由用户自己处理。

2 信令服务器的选型

Nodejs 的最大优点即是可以使用 JS 语言开发服务器程序,这样客户端和服务端就可以使用同一种语言开发。另一方面是JS语言比较简单。而且Nodejs服务端的功能库也比较多,可以用npm命令快速安装。

3 信令服务器环境搭建

3.1 搭建nodejs服务器

可以参考之前的博客Nodejs服务器搭建

3.2 安装socket.io
npm install socket.io

如果在CentOS上遇到报错

ERR! Error: CERT_UNTRUSTED
npm ERR!     at SecurePair.<anonymous> (tls.js:1430:32)
npm ERR!     at SecurePair.emit (events.js:92:17)
npm ERR!     at SecurePair.maybeInitFinished (tls.js:1029:10)
npm ERR!     at CleartextStream.read [as _read] (tls.js:521:13)
npm ERR!     at CleartextStream.Readable.read (_stream_readable.js:341:10)
npm ERR!     at EncryptedStream.write [as _write] (tls.js:418:25)
npm ERR!     at doWrite (_stream_writable.js:226:10)
npm ERR!     at writeOrBuffer (_stream_writable.js:216:5)
npm ERR!     at EncryptedStream.Writable.write (_stream_writable.js:183:11)
npm ERR!     at write (_stream_readable.js:602:24)
npm ERR!     at flow (_stream_readable.js:611:7)
npm ERR!     at Socket.pipeOnReadable (_stream_readable.js:643:5)
npm ERR! If you need help, you may report this log at:
npm ERR!     <http://github.com/isaacs/npm/issues>
npm ERR! or email it to:
npm ERR!     <npm-@googlegroups.com>

npm ERR! System Linux 2.6.32-696.16.1.el6.x86_64
npm ERR! command "node" "/usr/bin/npm" "install" "socket.io"
npm ERR! cwd /root/yeliang
npm ERR! node -v v0.10.48
npm ERR! npm -v 1.3.6
npm http GET https://registry.npmjs.org/socket.io-adapter
npm ERR! 
npm ERR! Additional logging details can be found in:
npm ERR!     /root/yeliang/npm-debug.log
npm ERR! not ok code 0

则尝试如下命令

npm config set strict-ssl false
3.3 在js中引入socket.io
var socketIo = require('socket.io');

4 信令服务器发送消息

4.1 给本次连接发消息
socket.emit()

示例:

socket.emit('joined', room, socket.id);
4.2 给某个房间内所有人发消息
io.in(room).emit()

这里的io实际上是监听https_server返回的对象

var io = socketIo.listen(https_server);

示例:

io.in(room).emit('joined', room, socket.id)
4.3 除本连接外,给某个房间内所有人发送消息
socket.to(room).emit()

示例:

socket.to(room).emit('otherjoin', room);//除自己之外
4.4 除本连接外,给所有人发送消息
socket.broadcast.emit()

示例:

socket.to(room).emit('joined', room, socket.id);

5 客户端使用socket.io处理消息

5.1 发送action命令

client发送:

socket.emit('action');

server接收:

socket.on('action', function(){...});
5.2 发送了action命令,并且有data数据

client发送:

socket.emit('action', data);

server接收:

socket.on('action', function(data){...});

示例:
client向server发送的action为leave

socket.emit('leave', room);

server端接收leave消息

socket.on('leave', (room)=> {
	logger.info("leave",",room = ", room,",socket.id = ", socket.id);
		var myRoom = https_socket.sockets.adapter.rooms[room];
		var users = Object.keys(myRoom.sockets).length;
		socket.leave(room);
		socket.to(room).emit('bye', room, socket.id)
	 	socket.emit('leaved', room, socket.id);	
});
5.3 发送action命令,并且有两个数据<

client发送:

socket.emit(action, arg1, arg2);

server接收:

socket.on('action', function(arg1, arg2){...});

示例:
client向server发送action为message

socket.emit('message', room, data);

server接收messag消息

socket.on('message', (room, data)=>{
		https_socket.to(room).emit('message', room, socket.id, data);
	});
5.4 发送action命令,在emit方法中包含回调函数

client发送:

socket.emit('action', data, function(arg1, arg2){...});

server接收:

socket.on('action',function(data, fn));

6 聊天室示例

1 完整功能展示

在这里插入图片描述

2 客户端完整代码
2.1 html部分
<html>
	<head>
		<title>Chat Room</title>
		<link rel="stylesheet" type="text/css" href="./css/main.css"></link>
	</head>
	<body>
		<table align="center"> 
			<tr>
				<td>
					<label>UserName:</label>
					<input type=text id="username"></input>
				</td>
			</tr>
			<tr>
				<td>
					<label>room:</label>
					<input type=text id="room"></input>
					<button id="connect">Connect</button>
					<button id="leave" disabled>Leave</button>
				</td>
			</tr>
			<tr>
				<td>
					<label>Content:</label><br>
					<textarea disabled style="line-height: 1.5;" id="output" rows="10" cols="100"></textarea>
				</td>
			</tr>
			<tr>
				<td>
					<label>Input:</label><br>
					<textarea disabled id="input" rows="3" cols="100"></textarea>
				</td>
			</tr>
			<tr>
				<td>
					<button id="send">Send</button>
				</td>
			</tr>
		</table>

		<script src = "./client.js"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
	</body>
</html>
2.2 js部分
'use strict'

var userName = document.querySelector('input#username');
var inputRoom = document.querySelector('input#room');
var btnConnect = document.querySelector('button#connect');
var btnLeave = document.querySelector('button#leave');
var outputArea = document.querySelector('textarea#output');
var inputArea = document.querySelector('textarea#input');
var btnSend = document.querySelector('button#send');

var socket;
var room;

btnConnect.onclick = () =>{
	//connect
	socket = io.connect();

	//receive message
	socket.on('joined', (room,id) =>{

		console.log('joined',"room = ", room, "id = ",id);
		btnConnect.disabled = true;
		btnLeave.disabled = false;
		inputArea.disabled = false;
		btnSend.disabled = false;
	});

	socket.on('leaved', (room, id)=>{
		console.log('leaved',"room = ", room, "id = ",id);

		btnConnect.disabled = false;
		btnLeave.disabled = true;
	    inputArea.disabled = true;
		btnSend.disabled = true;

		socket.disconnect();
	});

	socket.on('message',(room, id, data) =>{
		console.log('message',"room = ", room, "id = ",id, "data = " + data);

		outputArea.scrollTop = outputArea.scrollHeight;//窗口总是显示最后的内容
		outputArea.value = outputArea.value + data + '\r';
	});

	socket.on('disconnect', (socket)=>{
		console.log('disconnect');
		btnConnect.disabled = false;
		btnLeave.disabled = true;
	    inputArea.disabled = true;
	    btnSend.disabled = true;

	});

	room = inputRoom.value;
	socket.emit('join', room);
}

btnSend.onclick = ()=>{
	var data = inputArea.value;
	data = userName.value + ':' + data;
	socket.emit('message', room, data);
	inputArea.value = '';
}

btnLeave.onclick = ()=>{
	room = inputRoom.value;
	socket.emit('leave', room);
}

inputArea.onkeypress = (event)=>{
	if(event.keyCode == 13){
		var data = inputArea.value;
		data = userName.value + ":" + data;
		socket.emit('message', room, data);
		inputArea.value = '';
		event.preventDefault();//阻止默认行文
	}
}

当点击Connect连接服务器时,日志输出为

joined room =  111 id =  BMxGJ2ViQlARUtAGAAAB

在另外一个html页面的出入框中输入文字“嘎哈呢啊盖伦”,然后点击发送时日志输出:

message room =  111 id =  t6WAh4Cq5dikz_2CAAAA data = 德邦:嘎哈呢啊盖伦

点击leave按钮,日志输出:

leaved room =  111 id =  BMxGJ2ViQlARUtAGAAAB
disconnect
3 服务端完整代码
'use strict'
var https = require('https');
var fs = require('fs');

var express = require('express');
var serveIndex = require('serve-index');

//socket.io
var socketIo = require('socket.io');

//
var log4js = require('log4js');

log4js.configure({
    appenders: {
        file: {
            type: 'file',
            filename: 'app.log',
            layout: {
                type: 'pattern',
                pattern: '%r %p - %m',
            }
        }
    },
    categories: {
       default: {
          appenders: ['file'],
          level: 'debug'
       }
    }
});

var logger = log4js.getLogger();

var app = express();
app.use(serveIndex('./public'));
app.use(express.static('./public'));

var options = {
	key : fs.readFileSync('./cert/test.key'),
	cert: fs.readFileSync('./cert/test.pem')
}

//https server
var https_server = https.createServer(options, app);

//bind socket.io with https_server
var https_socket = socketIo.listen(https_server);

//connection
https_socket.sockets.on('connection', (socket)=>{
	logger.log("connection");
	
	socket.on('message', (room, data)=>{
		https_socket.to(room).emit('message', room, socket.id, data)//房间内所有人,除自己外
	});

	//该函数应该加锁
	socket.on('join', (room)=> {
		logger.log("join",",room = ", room,",socket.id = ", socket.id);
		socket.join(room);

		var myRoom = https_socket.sockets.adapter.rooms[room];
		var users = Object.keys(myRoom.sockets).length;

		logger.log('the number of user in room is: ' + users);

		//在这里可以控制进入房间的人数,现在一个房间最多 2个人
		//为了便于客户端控制,如果是多人的话,应该将目前房间里
		//人的个数当做数据下发下去。
		if(users < 3) {
			socket.emit('joined', room, socket.id);	
			if (users > 1) {
				socket.to(room).emit('otherjoin', room);//除自己之外
			}
		}else {
			socket.leave(room);
			socket.emit('full', room, socket.id);	
		}
	 	//socket.to(room).emit('joined', room, socket.id);//除自己之外
		//io.in(room).emit('joined', room, socket.id)//房间内所有人
	 	//socket.broadcast.emit('joined', room, socket.id);//除自己,全部站点	
	});

	socket.on('leave', (room)=> {
		logger.log("leave",",room = ", room,",socket.id = ", socket.id);
		var myRoom = https_socket.sockets.adapter.rooms[room];
		var users = Object.keys(myRoom.sockets).length;
		//users - 1;

		logger.log('the number of user in room is: ' + (users-1));

		socket.leave(room);
		socket.to(room).emit('bye', room, socket.id)//房间内所有人,除自己外
	 	socket.emit('leaved', room, socket.id);	
	 	//socket.to(room).emit('joined', room, socket.id);//除自己之外
		//io.in(room).emit('joined', room, socket.id)//房间内所有人
	 	//socket.broadcast.emit('joined', room, socket.id);//除自己,全部站点	
	});

});
https_server.listen(443, '0.0.0.0');
3.1 以下是各个事件的输出日志

连接:

[2019-10-17T14:34:30.815] [INFO] default - connection
[2019-10-17T14:34:30.819] [INFO] default - join ,room =  111 ,socket.id =  DZWaJaib0mzKy5ouAAAB

收到消息:

[2019-10-17T14:34:38.766] [INFO] default - receive message ,room =  111 ,data =  德邦:嘎哈呢啊盖伦

离开房间:

[2019-10-17T14:36:51.772] [INFO] default - leave ,room =  111 ,socket.id =  kczB-Ecky7J2t9UCAAAC
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值