了解如何使用 WebRTC、信令和 STUN/TURN 服务器在 Spring Boot 应用程序中启用点对点实时通信。
为什么选择 WebRTC + Spring Boot?
WebRTC 可在浏览器之间直接进行实时音频/视频流传输,延迟低,无需插件。但 WebRTC 需要一个信令服务器来交换连接设置数据(SDP、ICE candidates)。而 Spring Boot 可以使用 WebSocket 构建一个出色的信令服务器!
高级架构
- 两个客户端打开浏览器页面。
- 通过 WebSocket 连接到 Spring Boot 服务器。
- 播放音频/视频轨道。
- 使用 WebSocket 信令交换 SDP offers/answers 和 ICE candidates。
- WebRTC 建立点对点连接。
- 客户端直接流式传输音频/视频,服务器只充当协调者。
Client A — WebSocket Signalling — Spring Boot — WebSocket Signalling — Client B
↘ Peer-to-peer Media using WebRTC (audio/video)
服务器设置:Spring Boot + WebSocket
添加依赖项(Maven):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocket 配置:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/signal")
.setAllowedOrigins("*")
.withSockJS();
}
}
信令控制器:
@Controller
public class SignalingController {
@MessageMapping("/signal")
@SendTo("/topic/signals")
public SignalingMessage signaling(SignalingMessage message) {
return message;
}
}
信令消息的DTO:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SignalingMessage {
private String from;
private String to;
private String type; // "offer", "answer", "candidate"
private String sdp; // for offer/answer
private String candidate; // for ICE
}
客户端:JavaScript 代码
HTML:
<!DOCTYPE html>
<html>
<head>
<title>WebRTC Call</title>
</head>
<body>
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs/lib/stomp.min.js"></script>
<script src="webrtc.js"></script>
</body>
</html>
webrtc.js:
let localStream, peerConnection;
const config = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
// 连接到信令
const socket = new SockJS('/signal');
const stomp = Stomp.over(socket);
stomp.connect({}, () => {
stomp.subscribe('/topic/signals', onSignal);
});
// 从摄像机/麦克风获取媒体
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(stream => {
localStream = stream;
document.getElementById('localVideo').srcObject = stream;
createPeerConnection();
});
function createPeerConnection() {
peerConnection = new RTCPeerConnection(config);
// 将本地轨道添加到连接
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
peerConnection.ontrack = event => {
document.getElementById('remoteVideo').srcObject = event.streams[0];
};
peerConnection.onicecandidate = event => {
if (event.candidate) {
stomp.send("/app/signal", {}, JSON.stringify({
type: "candidate",
candidate: JSON.stringify(event.candidate)
}));
}
};
// 创建 offer 并发送
peerConnection.createOffer()
.then(offer => peerConnection.setLocalDescription(offer))
.then(() => {
stomp.send("/app/signal", {}, JSON.stringify({
type: "offer",
sdp: peerConnection.localDescription
}));
});
}
function onSignal(message) {
const msg = JSON.parse(message.body);
switch (msg.type) {
case "offer":
handleOffer(msg);
break;
case "answer":
peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp));
break;
case "candidate":
peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(msg.candidate)));
break;
}
}
function handleOffer(msg) {
createPeerConnection();
peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp))
.then(() => peerConnection.createAnswer())
.then(answer => peerConnection.setLocalDescription(answer))
.then(() => {
stomp.send("/app/signal", {}, JSON.stringify({
type: "answer",
sdp: peerConnection.localDescription
}));
});
}
其它
媒体:STUN/TURN 服务器
- STUN 服务器支持 NAT 穿越。在
RTCPeerConnection配置中使用像 Google 这样的公共服务器。 - 穿越 NAT/防火墙需要 TURN 服务器。在
iceServers配置中添加 TURN 服务器详细信息。
高级增强功能
- 使用TURN 服务器在受限网络之间实现可靠通信。
- 实施信令验证,防止未经授权的连接。
- 使用
getStats()跟踪呼叫统计信息。 - 通过网状或 SFU 结构添加群组呼叫支持。
- 使用 TLS (wss://) 进行部署,以确保信令安全。
常见陷阱和提示
- 在开始
.createOffer()之前,确保两个对等方都加载了客户端和信令。 - 浏览器行为不同;使用
onicecandidate事件。 - 切记将本地视频元素静音以避免回声。
- 通过
peerConnection.close()和信令处理干净的断开连接。
小结
本文仅用 Spring Boot 和浏览器原生 WebRTC 构建了一个简单的音频/视频通话系统。服务器只处理信令,而浏览器对等方则以 P2P 方式连接。这种设计具有良好的扩展性,并避免了服务器媒体处理。
在此基础上进行扩展,可以在 Java 技术栈上构建聊天应用程序、远程医疗服务或协作平台。
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/60013.html