You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ubuntu服务器部署类Zoom应用后Peer无法加入同一房间问题

类Zoom视频会议应用部署后Peer连接失败问题

我正在开发一款类Zoom视频会议应用,核心采用Node.js、WebRTC和PeerJS技术栈,参考Clever Programmer与Web Dev Simplified的教程实现。本地环境中多用户可正常加入指定房间,但部署到Ubuntu生产服务器后,Peer无法连接至同一房间。我推测问题根源是生产服务器未正常运行Peer Server。

在线演示地址:live.grandfoodproduct.com

Script.js

const socket = io('/')
const videoGrid = document.getElementById('video-grid')

const myPeer = new Peer(undefined,{
    host: '/',
    port:'8000'
})
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}

navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true
}).then(stream =>{
    addVideoStream(myVideo, stream)

    myPeer.on('call',call => {
        call.answer(stream)
        const video = document.createElement('video')
        call.on('stream', userVideoStream => {
            addVideoStream(video, userVideoStream)
        })
    })

    socket.on('user-connected', userId =>{
        connectToNewUser(userId, stream)
    })

    socket.on('user-disconnected', userId =>{
       if(peers[userId]) peers[userId].close()
    })

})

myPeer.on('open', id => {
    socket.emit('join-room', ROOM_ID, id)
})

function connectToNewUser(userId, stream){
    const call = myPeer.call(userId, stream)
    const video = document.createElement('video')
    call.on('stream', userVideoStream => {
        addVideoStream(video, userVideoStream)
    })
    call.on('close', () => {
        video.remove()
    })

    peers[userId] = call
}

function addVideoStream(video, stream){
    video.srcObject = stream
    video.addEventListener('loadedmetadata',() => {
        video.play()
    })
    videoGrid.append(video)
}

//====================================== Front-end styling logics =================================

let audio = document.getElementById('audio')
let audio_mute = document.getElementById('audio-mute')
let video = document.getElementById('video')
let video_mute = document.getElementById('video-mute')
let record = document.getElementById('record')
let record_stop = document.getElementById('record-stop')
let leave_btn = document.getElementById('leave-btn')

audio.addEventListener('click', function(){
    audio.style.display = 'none';
    audio_mute.style.display = 'inline-block';
})
audio_mute.addEventListener('click', function(){
    audio_mute.style.display = 'none';
    audio.style.display = 'inline-block';
})
video.addEventListener('click', function(){
    video.style.display = 'none';
    video_mute.style.display = 'inline-block';
})
video_mute.addEventListener('click', function(){
    video_mute.style.display = 'none';
    video.style.display = 'inline-block';
})
record.addEventListener('click', function(){
    record.style.display = 'none';
    record_stop.style.display = 'inline-block';
})
record_stop.addEventListener('click', function(){
    record_stop.style.display = 'none';
    record.style.display = 'inline-block';
})
leave_btn.addEventListener('click',function(){
    alert("Ki khobor?")
})

Server.js

const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const {v4: uuidV4} =require('uuid')

app.set('view engine', 'ejs')
app.use(express.static('public'))

app.get('/', (req, res) => {
    res.redirect(`/${uuidV4()}`)
})
app.get('/:room',(req,res) =>{
    res.render('room',{ roomId: req.params.room }) /* This is comming directly from the browser url */
})

io.on('connection', socket => {
    socket.on('join-room', (roomId, userId) => {
        socket.join(roomId)
        socket.to(roomId).emit('user-connected', userId) /* Here we emmited the broadcast to remove the error we were getting from the server */
        socket.on('disconnect',() =>{
            socket.to(roomId).emit('user-disconnected', userId)
        })
    })
})

server.listen(3001)

Room.ejs

<!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">
    <script>
        const ROOM_ID = "<%= roomId %>"
    </script>
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        clifford: '#da373d',
                    }
                }
            }
        }
    </script>
    <link href="https://cdn.jsdelivr.net/npm/daisyui@2.42.1/dist/full.css" rel="stylesheet" type="text/css" />
    <!-- <link rel="shortcut icon" href="/views/Assets/favicon.ico" type="image/x-icon"> -->
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="https://code.iconify.design/iconify-icon/1.0.1/iconify-icon.min.js"></script>
    <script defer src="https://unpkg.com/peerjs@1.4.7/dist/peerjs.min.js"></script>
    <script src="/socket.io/socket.io.js" defer></script>
    <script src="script.js" defer></script>
    <title>Arena | Class Room</title>
    <style>
        #video-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 5px;
            /* grid-template-columns: repeat(auto-fill, 400px); */
            /* grid-auto-rows: 400px; */
        }

        video {
            width: 100%;
            height: 100%;
            object-fit: cover;
            border: 3px solid rgb(230, 224, 224);
            border-radius: 10px;
        }

        .d-grid {
            display: grid;
            grid-template-columns: 1fr 11fr;
        }

        .chat-box {
            /* height: calc(100vh - 7%); */
            height: calc(100vh - 40px);
            
        }
        .message-box {
            height: calc(100vh - 93%);
            max-height: 40px;
        }
        .drawer-toggle:checked.drawer-side>.drawer-overlay{
            opacity: 1.0 !important;
        }
    </style>
</head>

<body class="bg-[linear-gradient(90deg,#56b5e3,#7d58a5)]">
    <!-- Chat box -->
    <div class="drawer drawer-end">
        <input id="my-drawer" type="checkbox" class="drawer-toggle" />
        <div class="drawer-content h-screen flex items-center justify-center opacity-100">


            <!-- =========================================== -->
            <!-- <div class="alert shadow-lg">
                <div>
                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-info flex-shrink-0 w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
                  <span>we use cookies for no reason.</span>
                </div>
                <div class="flex-none">
                  <button class="btn btn-sm btn-ghost">Deny</button>
                  <button class="btn btn-sm btn-primary">Accept</button>
                </div>
              </div> -->
            <!-- =========================================== -->


            <!-- Page content here -->
            <div id="video-grid"> </div>
            <div class="absolute bottom-0 grid grid-cols-3 justify-between items-center w-full bg-[linear-gradient(90deg,#74adc9,#8b70a9)] py-2">
                <div class="ml-3 flex justify-start gap-4">
                    <iconify-icon id="audio" icon="ant-design:audio-outlined" class="text-white rounded-full text-2xl hover:cursor-pointer"></iconify-icon>
                    <iconify-icon id="audio-mute" icon="ant-design:audio-muted-outlined" class="text-white rounded-full text-2xl hidden hover:cursor-pointer"></iconify-icon>
                    <!-- <iconify-icon id="video" icon="ph:video-camera-fill" class="bg-black text-white rounded-full text-2xl p-4"></iconify-icon> -->
                    <iconify-icon id="video" icon="ion:videocam" class="text-white rounded-full text-2xl hover:cursor-pointer"></iconify-icon>
                    <!-- <iconify-icon id="video-mute" icon="ph:video-camera-slash-fill" class="bg-black text-white rounded-full text-2xl p-4 hidden"></iconify-icon> -->
                    <iconify-icon id="video-mute" icon="ion:videocam-off" class="text-white rounded-full text-2xl hidden hover:cursor-pointer"></iconify-icon>
                </div>
                <div class="text-center flex justify-center gap-[40px]">
                    <iconify-icon icon="fluent:people-team-32-filled" class="text-white rounded-full text-2xl hover:cursor-pointer"></iconify-icon>
                    <label for="my-drawer" class="drawer-button"><iconify-icon icon="mdi:message-reply" flip="horizontal" class="text-white rounded-full text-2xl hover:cursor-pointer"></iconify-icon></label>
                    <iconify-icon icon="teenyicons:screen-solid" class="text-white rounded-full text-2xl hover:cursor-pointer"></iconify-icon>
                    <iconify-icon id="record" icon="ph:record-fill" class="text-white rounded-full text-2xl hover:cursor-pointer"></iconify-icon>
                    <iconify-icon id="record-stop" icon="fluent:record-stop-32-filled" class="text-white rounded-full text-2xl hidden hover:cursor-pointer"></iconify-icon>
                </div>
                <div class="text-end mr-10">
                    <button id="leave-btn" class="bg-red-700 text-white rounded-lg p-2">Leave Room</button>
                </div>
            </div>
        </div>
        <div class="drawer-side">
            <label for="my-drawer" class="drawer-overlay"></label>
            <ul class="menu w-80 bg-base-100 text-base-content">
                <!-- Sidebar content here -->
                <div class="p-3 chat-box">
                    <div class="flex">
                        <div class="avatar placeholder w-2/12 h-2/12">
                            <div class="bg-neutral-focus text-neutral-content rounded-full w-24">
                                <span class="text-xl">K</span>
                            </div>
                        </div>
                        <div>
                            <h1>Person Name here</h1>
                            <p>Lorem ipsum dolor sit amet . </p>
                        </div>
                    </div>
                </div>

                <div class="sticky bottom-0 border border-blue-300 flex items-center justify-between message-box">
                    <input type="text" class="w-11/12 focus:outline-none pl-3" placeholder="Type your message here">
                    <iconify-icon icon="carbon:send-filled"
                        class="text-right w-1/12 text-blue-500 hover:cursor-pointer text-2xl"></iconify-icon>
                </div>
            </ul>
        </div>
    </div>
</body>

</html>

任何形式的帮助都将不胜感激,提前致谢!


内容的提问来源于stack exchange,提问作者Mehedi Hasan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 20:45:31