在 WebRTC 中,信令是实现点对点通信的必要步骤。而 Socket.io 是一种基于事件驱动的实时通信框架,可以很好地用来实现 WebRTC 中的信令功能。本文将介绍如何使用 Socket.io 实现 WebRTC 中的信令功能,包括建立连接、发送和接收消息等。
建立连接
在使用 Socket.io 实现 WebRTC 中的信令功能时,首先需要建立连接。建立连接的过程可以分为客户端和服务器端两个部分。
客户端
客户端需要引入 Socket.io 的客户端库,并通过该库创建一个 Socket 对象。代码如下:
// 引入 Socket.io 的客户端库
import io from 'socket.io-client';
// 创建 Socket 对象
const socket = io('http://localhost:3000');其中,http://localhost:3000 是服务器端的地址和端口号,需要根据实际情况进行修改。
服务器端
服务器端也需要引入 Socket.io 的库,并创建一个 Socket 对象。代码如下:
// 引入 Socket.io 的库
const io = require('socket.io')(server);
// 监听连接事件
io.on('connection', socket => {
console.log('a user connected');
});其中,server 是一个已经创建好的 HTTP 服务器对象,需要将其传递给 Socket.io 的构造函数。
发送和接收消息
建立了连接之后,客户端和服务器端就可以相互发送消息了。在 WebRTC 中,信令消息通常包括会话描述和候选者信息两部分。
发送消息
客户端可以通过 Socket 对象的 emit 方法向服务器端发送消息。例如,向服务器端发送会话描述信息的代码如下:
// 发送会话描述信息
socket.emit('offer', {
sdp: offer.sdp,
type: offer.type
});其中,offer 是一个包含会话描述信息的对象,sdp 和 type 分别是会话描述信息的 SDP 和类型。
接收消息
服务器端可以通过监听客户端发送的消息事件来接收消息。例如,监听客户端发送的会话描述信息的代码如下:
// 监听客户端发送的会话描述信息
socket.on('offer', offer => {
console.log('received offer:', offer);
});其中,offer 是客户端发送的包含会话描述信息的对象。
示例代码
下面是一个完整的示例代码,演示了如何使用 Socket.io 实现 WebRTC 中的信令功能。该示例代码基于 React 和 Node.js,需要先安装相关依赖:
npm install --save react react-dom socket.io-client express socket.io
客户端代码:
// www.javascriptcn.com code example
import React, { useState, useEffect, useRef } from 'react';
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
function App() {
const [localStream, setLocalStream] = useState(null);
const [remoteStream, setRemoteStream] = useState(null);
const [peerConnection, setPeerConnection] = useState(null);
const [inputValue, setInputValue] = useState('');
const localVideoRef = useRef(null);
const remoteVideoRef = useRef(null);
useEffect(() => {
// 获取本地媒体流
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
setLocalStream(stream);
localVideoRef.current.srcObject = stream;
})
.catch(error => {
console.error('getUserMedia error:', error);
});
// 监听 ICE 候选者事件
socket.on('iceCandidate', event => {
console.log('received iceCandidate:', event);
if (event.candidate) {
peerConnection.addIceCandidate(event.candidate)
.then(() => {
console.log('addIceCandidate success');
})
.catch(error => {
console.error('addIceCandidate error:', error);
});
}
});
// 监听会话描述事件
socket.on('sessionDescription', event => {
console.log('received sessionDescription:', event);
const { sdp, type } = event;
const remoteDescription = new RTCSessionDescription({ sdp, type });
peerConnection.setRemoteDescription(remoteDescription)
.then(() => {
console.log('setRemoteDescription success');
if (type === 'offer') {
peerConnection.createAnswer()
.then(answer => {
console.log('createAnswer success:', answer);
peerConnection.setLocalDescription(answer)
.then(() => {
console.log('setLocalDescription success');
socket.emit('sessionDescription', {
sdp: answer.sdp,
type: answer.type
});
})
.catch(error => {
console.error('setLocalDescription error:', error);
});
})
.catch(error => {
console.error('createAnswer error:', error);
});
}
})
.catch(error => {
console.error('setRemoteDescription error:', error);
});
});
// 创建 PeerConnection 对象
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
{ urls: 'stun:stun2.l.google.com:19302' },
{ urls: 'stun:stun3.l.google.com:19302' },
{ urls: 'stun:stun4.l.google.com:19302' }
]
});
setPeerConnection(pc);
// 添加本地媒体流
if (localStream) {
localStream.getTracks().forEach(track => {
pc.addTrack(track, localStream);
});
}
// 监听 ICE 连接状态变化事件
pc.addEventListener('iceconnectionstatechange', event => {
console.log('iceconnectionstatechange:', pc.iceConnectionState);
if (pc.iceConnectionState === 'connected') {
console.log('connected');
}
else if (pc.iceConnectionState === 'disconnected') {
console.log('disconnected');
}
else if (pc.iceConnectionState === 'failed') {
console.log('failed');
}
});
// 监听远程媒体流事件
pc.addEventListener('track', event => {
console.log('track:', event);
setRemoteStream(event.streams[0]);
remoteVideoRef.current.srcObject = event.streams[0];
});
}, []);
const handleSendOffer = () => {
peerConnection.createOffer()
.then(offer => {
console.log('createOffer success:', offer);
peerConnection.setLocalDescription(offer)
.then(() => {
console.log('setLocalDescription success');
socket.emit('sessionDescription', {
sdp: offer.sdp,
type: offer.type
});
})
.catch(error => {
console.error('setLocalDescription error:', error);
});
})
.catch(error => {
console.error('createOffer error:', error);
});
};
const handleSendIceCandidate = () => {
peerConnection.addEventListener('icecandidate', event => {
console.log('icecandidate:', event);
if (event.candidate) {
socket.emit('iceCandidate', {
candidate: event.candidate
});
}
});
};
const handleInputChange = event => {
setInputValue(event.target.value);
};
const handleSendMessage = () => {
socket.emit('message', inputValue);
};
return (
<div>
<video ref={localVideoRef} autoPlay muted></video>
<video ref={remoteVideoRef} autoPlay></video>
<button onClick={handleSendOffer}>Send Offer</button>
<button onClick={handleSendIceCandidate}>Send IceCandidate</button>
<input type="text" value={inputValue} onChange={handleInputChange}></input>
<button onClick={handleSendMessage}>Send Message</button>
</div>
);
}
export default App;服务器端代码:
// www.javascriptcn.com code example
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
io.on('connection', socket => {
console.log('a user connected');
// 监听会话描述消息
socket.on('sessionDescription', event => {
console.log('received sessionDescription:', event);
socket.broadcast.emit('sessionDescription', event);
});
// 监听 ICE 候选者消息
socket.on('iceCandidate', event => {
console.log('received iceCandidate:', event);
socket.broadcast.emit('iceCandidate', event);
});
// 监听消息
socket.on('message', message => {
console.log('received message:', message);
socket.broadcast.emit('message', message);
});
// 监听断开连接事件
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
server.listen(3000, () => {
console.log('listening on *:3000');
});在运行完上述代码后,可以打开两个浏览器窗口,分别访问 http://localhost:3000,然后点击 Send Offer 按钮,即可建立点对点连接并进行视频通话。也可以点击 Send IceCandidate 按钮,发送 ICE 候选者信息,以提高连接成功率。同时,还可以在输入框中输入消息,点击 Send Message 按钮,发送消息给对方。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d18eefa941bf713434370c