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

如何用HTML5实现双人会议式摄像头收发?连接关闭问题排查

问题

我参考Tutorial Point的代码片段,尝试实现类似双人会议的HTML5摄像头收发功能,但连接总是自动关闭。在单台电脑上测试本地收发时发现:代码绑定一个端口后接收端也绑定同一端口;若不绑定同一端口,发送端又缺少目标端口定义。我尝试了123、80、10000端口,且已通过gpedit的IP安全策略允许这些端口,但仍提示连接关闭。

代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta name="generator" content=
  "HTML Tidy for Linux (vers 25 March 2009), see www.w3.org" />
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js" type=
  "text/javascript">
</script>
  <script type="text/javascript">
</script>

  <title>webpage</title>
  <style type="text/css">
/*<![CDATA[*/
  body.c2 {background-color:black;text-align:center}
  div.c1 {position:absolute;}
  /*]]>*/
  </style>
</head>

<body class="c2">
  <div id="d4" class="c1">
    <p><input type="button" id="stop" onclick="stop()" /> Stop Cam <input type="button"
    id="start" onclick="start()" /> Start Cam <input type="button" id="run" onclick=
    "WebSocketTest()" /> Start Cam</p>
  </div><script type="text/javascript">
//<![CDATA[
  function drawImge(){
    var video = document.querySelector("#video");
    var canvas = document.querySelector("#videoCanvas");
    var ctx = canvas.getContext('2d');

    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;


    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

    var faceArea = 300;
    var pX=canvas.width/2 - faceArea/2;
    var pY=canvas.height/2 - faceArea/2;

    ctx.rect(pX,pY,faceArea,faceArea);
    ctx.lineWidth = "2";
    ctx.strokeStyle = "green";    
    ctx.stroke();


    setTimeout(drawImge , 100);
  }
  var video = document.querySelector("#video");
  video.onplay = function() {
  setTimeout(drawImge , 300);
  };

   var stop = function () {
      var stream = video.srcObject;
      var tracks = stream.getTracks();
      for (var i = 0; i < tracks.length; i++) {
        var track = tracks[i];
        track.stop();
      }
      video.srcObject = null;
    }
    var start = function () {
      var video = document.getElementById('video'),
        vendorUrl = window.URL || window.webkitURL;
      if (navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({ video: true })
          .then(function (stream) {
            video.srcObject = stream;

            if ("WebSocket" in window) {
               alert("WebSocket is supported by your Browser!");
               
               // Let us open a web socket
               var ws = new WebSocket("ws://127.0.0.1:123/echo");
               var wss = new WebSocket("ws://127.0.0.1:80/echo");
               wss.onopen = function() {
                  
                  // Web Socket is connected, send data using send()
                  wss.send(stream);
                  
               };
               wss.onmessage = function (evt) {
                  var received_msg = evt.data;
                  
               };
               wss.onclose = function() {
                  
                  // websocket is closed.
                  alert("Connection is closed...");
               };
            } else {
              
               // The browser doesn't support WebSocket
               alert("WebSocket NOT supported by your Browser!");
            }

          }).catch(function (error) {
            console.log("Something went wrong!");
          });
      }
    }
    $(function () {
      start();
    });



    var WebSocketTest = function () {
            if ("WebSocket" in window) {
               alert("WebSocket is supported by your Browser!");
               
               // Let us open a web socket
               var ws = new WebSocket("ws://127.0.0.1:123/echo");

               ws.onopen = function() {
                  
                  // Web Socket is connected, send data using send()
                  ws.send("Message to send");
                  alert("Message is sent...");
               };
               ws.onmessage = function (evt) {
                  var received_msg = evt.data;
                  alert("Message is received...");
               };
               ws.onclose = function() {                   
                  // websocket is closed.
                  alert("Connection is closed...");
               };
            } else {
              
               // The browser doesn't support WebSocket
               alert("WebSocket NOT supported by your Browser!");
            }
    }
    $(function () {
      WebSocketTest();
    });

  //]]>
  </script>
</body>
</html>
解决方案

核心问题分析

  • 缺少WebSocket服务端:代码仅实现了WebSocket客户端,但没有对应服务端监听指定端口。浏览器发起连接后无服务端响应,连接会直接关闭。
  • 媒体流传输方式错误:直接用wss.send(stream)发送MediaStream对象无效,WebSocket无法直接传输原始媒体流,需借助WebRTC的RTCPeerConnection实现点对点音视频传输,WebSocket仅用于交换信令(如SDP、ICE候选)。
  • 端口逻辑混乱:本地测试无需绑定多个端口,WebRTC是点对点连接,WebSocket作为信令通道只需一个服务端端口即可。

修复步骤

  1. 搭建WebSocket服务端
    用Node.js快速实现简单信令服务(先安装ws包:npm install ws):

    const WebSocket = require('ws');
    const wss = new WebSocket.Server({ port: 8080 });
    
    wss.on('connection', (ws) => {
      ws.on('message', (data) => {
        // 转发信令给其他在线客户端
        wss.clients.forEach((client) => {
          if (client !== ws && client.readyState === WebSocket.OPEN) {
            client.send(data);
          }
        });
      });
    });
    

    启动服务后,客户端统一连接ws://127.0.0.1:8080。

  2. 改用WebRTC实现音视频传输
    替换原WebSocket传输媒体流的逻辑:

    • 客户端通过WebSocket交换SDP Offer/Answer和ICE候选信息
    • 创建RTCPeerConnection实例,将本地摄像头流添加到连接中,远端流渲染到video标签展示
  3. 修正端口使用
    所有客户端连接同一个WebSocket服务端端口(如8080),WebRTC会自动协商点对点传输的媒体端口,无需手动指定多个端口。

关键注意事项

  • 本地测试时,确保WebSocket服务端正常运行,可通过浏览器控制台(F12→网络→WebSocket)查看连接错误详情
  • WebRTC在localhost环境下无需HTTPS,但生产环境必须使用HTTPS
  • 避免使用系统保留端口(如80、123),选择1024以上的非知名端口(如8080、3000)

内容的提问来源于stack exchange,提问作者Ho Yeung - Lee-Maiva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:22:04