欢迎光临
我们一直在努力

Ubuntu 24.04 安装开源WebRTC信令服务器

Ubuntu 24.04 安装开源WebRTC信令服务器

前言

本指南提供了在Ubuntu 24.04环境下安装和配置三种流行的开源WebRTC信令服务器的详细步骤:

  • Janus – 功能丰富的通用WebRTC服务器
  • MediaSoup – 高性能的WebRTC选择性转发单元(SFU)
  • Simple-Peer-Server – 轻量级WebSocket信令服务器
  • 1. 系统准备

    更新系统包

    sudo apt update
    sudo apt upgrade -y

    安装通用依赖

    sudo apt install -y build-essential git curl wget gnupg2 dirmngr

    2. 安装Node.js(用于MediaSoup和Simple-Peer-Server)

    # 添加Node.js源
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash

    # 安装Node.js
    sudo apt install -y nodejs

    # 验证安装
    node -v
    npm -v

    3. 安装Janus Gateway

    安装依赖

    sudo apt install -y libmicrohttpd-dev libjansson-dev \\
    libssl-dev libsrtp-dev libsofia-sip-ua-dev libglib2.0-dev \\
    libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev \\
    libconfig-dev pkg-config gengetopt libtool automake libnice-dev

    下载并编译Janus

    # 克隆代码
    cd ~
    git clone https://github.com/meetecho/janus-gateway.git
    cd janus-gateway

    git checkout v1.0.7 # 使用稳定版本

    # 编译安装
    ./autogen.sh
    ./configure –prefix=/opt/janus
    make -j$(nproc)
    sudo make install
    sudo make configs

    配置Janus

    # 编辑配置文件
    sudo nano /opt/janus/etc/janus/janus.jcfg

    # 在nat_1_1_mapping字段添加服务器公网IP(如果是云服务器)
    # nat_1_1_mapping = "your_server_public_ip"

    # 启用HTTPS(可选但推荐)
    sudo nano /opt/janus/etc/janus/janus.transport.http.jcfg
    # 将secure = false 改为 secure = true
    # 配置cert_pem和key_pem指向SSL证书文件

    创建系统服务

    sudo nano /etc/systemd/system/janus.service

    添加以下内容:

    [Unit]
    Description=Janus WebRTC Server
    After=network.target

    [Service]
    ExecStart=/opt/janus/bin/janus
    Restart=on-failure
    RestartSec=5
    User=root

    [Install]
    WantedBy=multi-user.target

    启用并启动服务:

    sudo systemctl daemon-reload
    sudo systemctl enable janus
    sudo systemctl start janus

    4. 安装MediaSoup

    创建项目目录

    mkdir -p ~/mediasoup-server
    cd ~/mediasoup-server

    初始化项目

    npm init -y

    安装MediaSoup及依赖

    npm install mediasoup mediasoup-client express socket.io

    创建基本服务器文件

    创建server.js文件:

    nano server.js

    添加以下内容:

    const express = require('express');
    const https = require('https');
    const fs = require('fs');
    const { Server } = require('socket.io');
    const mediasoup = require('mediasoup');

    const app = express();

    // 静态文件服务
    app.use(express.static('public'));

    // HTTPS服务器配置
    const options = {
    key: fs.readFileSync('/path/to/your/key.pem'),
    cert: fs.readFileSync('/path/to/your/cert.pem')
    };

    const httpsServer = https.createServer(options, app);
    const io = new Server(httpsServer);

    // 房间管理
    const rooms = new Map();

    // MediaSoup配置
    const mediaCodecs = [
    {
    kind: 'audio',
    mimeType: 'audio/opus',
    clockRate: 48000,
    channels: 2
    },
    {
    kind: 'video',
    mimeType: 'video/VP8',
    clockRate: 90000
    }
    ];

    io.on('connection', async (socket) => {
    console.log('新客户端连接:', socket.id);

    // 房间创建或加入
    socket.on('createRoom', async (callback) => {
    try {
    const router = await mediasoup.createRouter({ mediaCodecs });
    const roomId = Math.random().toString(36).substring(2, 15);

    rooms.set(roomId, {
    router,
    peers: new Map()
    });

    callback({ roomId });
    } catch (error) {
    console.error('创建房间失败:', error);
    callback({ error: error.message });
    }
    });

    socket.on('joinRoom', async ({ roomId }, callback) => {
    try {
    const room = rooms.get(roomId);
    if (!room) {
    return callback({ error: '房间不存在' });
    }

    socket.join(roomId);
    callback({ success: true });
    } catch (error) {
    console.error('加入房间失败:', error);
    callback({ error: error.message });
    }
    });

    // WebRTC信令处理
    socket.on('getRtpCapabilities', ({ roomId }, callback) => {
    const room = rooms.get(roomId);
    if (!room) return callback({ error: '房间不存在' });

    callback({ rtpCapabilities: room.router.rtpCapabilities });
    });

    socket.on('createWebRtcTransport', async ({ roomId }, callback) => {
    try {
    const room = rooms.get(roomId);
    if (!room) return callback({ error: '房间不存在' });

    const transport = await room.router.createWebRtcTransport({
    listenIps: [{
    ip: '0.0.0.0',
    announcedIp: process.env.ANNOUNCED_IP || '127.0.0.1'
    }],
    enableUdp: true,
    enableTcp: true,
    preferUdp: true
    });

    callback({
    id: transport.id,
    iceParameters: transport.iceParameters,
    iceCandidates: transport.iceCandidates,
    dtlsParameters: transport.dtlsParameters
    });

    room.peers.set(socket.id, { transport });
    } catch (error) {
    console.error('创建WebRTC传输失败:', error);
    callback({ error: error.message });
    }
    });

    // 断开连接
    socket.on('disconnect', () => {
    console.log('客户端断开连接:', socket.id);
    // 清理资源
    });
    });

    // 启动服务器
    const PORT = process.env.PORT || 3000;
    httpsServer.listen(PORT, () => {
    console.log(`MediaSoup服务器运行在 https://localhost:${PORT}`);
    });

    创建启动脚本

    nano start.sh

    添加以下内容:

    #!/bin/bash

    # 设置环境变量
    export ANNOUNCED_IP=127.0.0.1 # 替换为你的服务器IP

    # 启动服务器
    node server.js

    设置执行权限:

    chmod +x start.sh

    5. 安装Simple-Peer-Server(轻量级信令服务器)

    创建项目目录

    mkdir -p ~/simple-peer-server
    cd ~/simple-peer-server

    初始化项目

    npm init -y

    安装依赖

    npm install ws uuid

    创建服务器文件

    nano server.js

    添加以下内容:

    const WebSocket = require('ws');
    const { v4: uuidv4 } = require('uuid');
    const https = require('https');
    const fs = require('fs');

    // 房间管理
    const rooms = new Map();

    // HTTPS配置
    const options = {
    key: fs.readFileSync('/path/to/your/key.pem'),
    cert: fs.readFileSync('/path/to/your/cert.pem')
    };

    // 创建HTTPS服务器
    const server = https.createServer(options);
    const wss = new WebSocket.Server({ server });

    // WebSocket连接处理
    wss.on('connection', (ws, req) => {
    // 为每个连接生成唯一ID
    const userId = uuidv4();
    console.log(`新用户连接: ${userId}`);

    // 存储用户信息
    ws.userId = userId;
    ws.roomId = null;

    // 消息处理
    ws.on('message', (message) => {
    try {
    const data = JSON.parse(message);

    switch (data.type) {
    case 'join_room':
    handleJoinRoom(ws, data.roomId, data.userName);
    break;

    case 'offer':
    case 'answer':
    case 'ice_candidate':
    // 转发WebRTC信令
    forwardSignaling(ws, data);
    break;

    case 'leave_room':
    handleLeaveRoom(ws);
    break;

    default:
    console.log(`未知消息类型: ${data.type}`);
    }
    } catch (error) {
    console.error('消息处理错误:', error);
    }
    });

    // 断开连接处理
    ws.on('close', () => {
    handleDisconnect(ws);
    });

    // 错误处理
    ws.on('error', (error) => {
    console.error(`WebSocket错误 (${userId}):`, error);
    });
    });

    // 处理加入房间
    function handleJoinRoom(ws, roomId, userName) {
    // 离开当前房间(如果在其他房间中)
    if (ws.roomId) {
    handleLeaveRoom(ws);
    }

    // 加入新房间
    if (!rooms.has(roomId)) {
    rooms.set(roomId, new Map());
    }

    const room = rooms.get(roomId);
    room.set(ws.userId, {
    ws,
    userName: userName || `用户${ws.userId.substring(0, 8)}`
    });

    ws.roomId = roomId;

    // 通知房间内其他用户
    const userList = [];
    room.forEach((user, id) => {
    if (id !== ws.userId) {
    userList.push({ id, userName: user.userName });

    // 通知其他用户新用户加入
    user.ws.send(JSON.stringify({
    type: 'user_joined',
    user_id: ws.userId,
    nickname: userName || `用户${ws.userId.substring(0, 8)}`
    }));
    }
    });

    // 发送房间内用户列表给新用户
    ws.send(JSON.stringify({
    type: 'room_joined',
    room_id: roomId,
    users: userList,
    user_id: ws.userId
    }));

    console.log(`${userName || `用户${ws.userId.substring(0, 8)}`} 加入房间 ${roomId}`);
    }

    // 处理离开房间
    function handleLeaveRoom(ws) {
    if (!ws.roomId) return;

    const room = rooms.get(ws.roomId);
    if (!room) return;

    const user = room.get(ws.userId);
    room.delete(ws.userId);

    // 如果房间为空,删除房间
    if (room.size === 0) {
    rooms.delete(ws.roomId);
    } else {
    // 通知其他用户
    room.forEach((u) => {
    u.ws.send(JSON.stringify({
    type: 'user_left',
    user_id: ws.userId,
    nickname: user ? user.userName : `用户${ws.userId.substring(0, 8)}`
    }));
    });
    }

    console.log(`${user ? user.userName : `用户${ws.userId.substring(0, 8)}`} 离开房间 ${ws.roomId}`);
    ws.roomId = null;
    }

    // 处理断开连接
    function handleDisconnect(ws) {
    console.log(`用户断开连接: ${ws.userId}`);
    handleLeaveRoom(ws);
    }

    // 转发WebRTC信令
    function forwardSignaling(ws, data) {
    if (!ws.roomId) {
    ws.send(JSON.stringify({
    type: 'error',
    message: '必须先加入房间'
    }));
    return;
    }

    const room = rooms.get(ws.roomId);
    if (!room) return;

    // 添加发送者信息
    data.sender_id = ws.userId;
    data.from_user_id = ws.userId;
    data.sender_name = room.get(ws.userId)?.userName || `用户${ws.userId.substring(0, 8)}`;
    data.from_user_name = data.sender_name;

    // 支持两种目标用户ID字段名
    const targetUserId = data.target_user_id || data.recipient_id;
    if (!targetUserId) {
    ws.send(JSON.stringify({
    type: 'error',
    message: '缺少目标用户ID'
    }));
    return;
    }

    const targetUser = room.get(targetUserId);
    if (!targetUser) {
    ws.send(JSON.stringify({
    type: 'error',
    message: '目标用户不在房间中'
    }));
    return;
    }

    // 转发消息给目标用户
    targetUser.ws.send(JSON.stringify(data));
    }

    // 启动服务器
    const PORT = process.env.PORT || 8443;
    server.listen(PORT, () => {
    console.log(`信令服务器运行在 https://0.0.0.0:${PORT}`);
    });

    创建启动脚本

    nano start.sh

    添加以下内容:

    #!/bin/bash

    # 启动服务器
    node server.js

    设置执行权限:

    chmod +x start.sh

    6. 与当前项目集成

    修改前端连接配置

    修改/home/lhz/meeting/static/js/app.js中的WebSocket连接URL:

    // 替换initWebSocket函数中的连接URL
    function initWebSocket() {
    // 使用Simple-Peer-Server
    const wsUrl = 'wss://your-server-ip:8443';
    // 或使用Janus
    // const wsUrl = 'wss://your-server-ip:8188';

    state.socket = new WebSocket(wsUrl);

    // 其余代码保持不变…
    }

    配置ICE服务器

    确保ICE_SERVERS配置在server.py和前端代码中一致:

    # 在server.py中
    ICE_SERVERS = [
    RTCIceServer(urls='stun:stun.l.google.com:19302'),
    RTCIceServer(urls='stun:stun1.l.google.com:19302'),
    # 添加自己的TURN服务器配置
    RTCIceServer(
    urls='turn:your-turn-server.com:443?transport=tcp',
    username='your-username',
    credential='your-credential'
    )
    ]

    7. 生成SSL证书

    对于HTTPS/WSS连接,需要SSL证书:

    # 生成自签名证书(开发环境)
    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

    # 将证书复制到需要的位置
    mkdir -p ~/ssl
    cp key.pem cert.pem ~/ssl/

    8. 防火墙配置

    # 开放必要的端口
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw allow 3478/udp # STUN/TURN端口
    sudo ufw allow 49152:65535/udp # WebRTC媒体端口范围
    sudo ufw reload

    9. 监控和日志

    Janus日志

    # 查看Janus日志
    sudo journalctl -u janus -f

    MediaSoup/Simple-Peer-Server日志

    可以使用pm2来管理Node.js进程并查看日志:

    # 安装pm2
    sudo npm install -g pm2

    # 启动服务并设置开机自启
    cd ~/simple-peer-server
    pm2 start server.js –name signaling-server
    pm2 startup
    pm2 save

    # 查看日志
    pm2 logs signaling-server

    10. 性能优化建议

  • 调整服务器资源限制:

    sudo nano /etc/security/limits.conf
    # 添加以下行
    * soft nofile 65535
    * hard nofile 65535

  • 优化内核参数:

    sudo nano /etc/sysctl.conf
    # 添加以下行
    net.core.rmem_max = 16777216
    net.core.wmem_max = 16777216
    net.ipv4.tcp_rmem = 4096 87380 16777216
    net.ipv4.tcp_wmem = 4096 65536 16777216
    net.ipv4.tcp_no_metrics_save = 1
    net.ipv4.tcp_window_scaling = 1
    net.ipv4.tcp_timestamps = 1
    net.ipv4.tcp_sack = 1
    net.core.netdev_max_backlog = 5000

    应用配置:

    sudo sysctl -p

  • 11. 推荐方案总结

  • 轻量级部署:选择 Simple-Peer-Server

    • 优点:简单易配置,资源占用少,与当前项目架构相似
    • 适用:小型会议系统,开发环境
  • 中等规模部署:选择 Janus

    • 优点:功能全面,文档完善,社区活跃
    • 适用:中小规模生产环境,需要多种WebRTC功能
  • 大规模高并发:选择 MediaSoup

    • 优点:高性能,可扩展性好,支持大规模并发
    • 适用:大规模生产环境,需要处理大量并发连接
  • 根据您的实际需求和服务器资源选择合适的方案。

    赞(0)
    未经允许不得转载:171主机测评 » Ubuntu 24.04 安装开源WebRTC信令服务器
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址