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

如何通过socket.io实现单客户端向多客户端实时传输音频流

实现整体思路

你选择的Socket.IO中转广播方案可以很好避免WebRTC点对点下发送端带宽随收听人数线性增长的问题,核心实现逻辑分为三步:发送端通过音频处理节点实时采集原始PCM音频数据,通过Socket.IO发送到服务端;服务端直接将音频数据广播给所有需要收听的客户端;接收端拿到音频数据后通过Web Audio API实时播放即可。


1. 发送端改造(原代码修改)

在原有本地播放逻辑的基础上新增ScriptProcessorNode节点采集实时音频数据,直接传输二进制数据保证传输效率:

$(document).ready(function () {
    var socket = io("ws://127.0.0.1:4385");
    // 缓冲区大小越小延迟越低,可根据实际网络情况调整,单声道配置可将数据量减半
    const BUFFER_SIZE = 4096;
    const SAMPLE_RATE = 44100;

    if (navigator.mediaDevices) {
        console.log('getUserMedia supported.');
        var constraints = { audio: true, sampleRate: SAMPLE_RATE };

        navigator.mediaDevices.getUserMedia(constraints)
            .then(function (stream) {
                let ctx = new AudioContext({ sampleRate: SAMPLE_RATE });
                let source = ctx.createMediaStreamSource(stream);
                // 创建音频处理节点采集原始数据
                let processor = ctx.createScriptProcessor(BUFFER_SIZE, 1, 1);
                
                source.connect(processor);
                processor.connect(ctx.destination); // 不需要本地播放可删掉这行

                // 监听音频缓冲区事件,拿到原始PCM数据
                processor.onaudioprocess = function(e) {
                    const audioData = e.inputBuffer.getChannelData(0);
                    // 直接传输二进制ArrayBuffer,效率远高于base64
                    socket.emit('audio-stream', audioData.buffer);
                };
            })
            .catch(function (err) {
                console.log('The following error occurred: ' + err);
            })
    }
});

2. Socket.IO服务端逻辑

服务端仅做中转广播,不需要对音频数据做任何处理,以下是Node.js版本示例:

const { Server } = require("socket.io");
const io = new Server(4385, {
  cors: { origin: "*" } // 生产环境请替换为允许的业务域名
});

io.on('connection', (socket) => {
  // 收到发送端的音频流,广播给除发送端外所有连接客户端
  socket.on('audio-stream', (audioBuffer) => {
    socket.broadcast.emit('audio-play', audioBuffer);
    // 多房间场景下可改为指定房间广播:io.to('房间ID').emit('audio-play', audioBuffer)
  });
});

3. 接收端播放逻辑

接收端维护音频数据队列解决网络抖动问题,拿到数据后实时解码播放:

$(document).ready(function () {
    var socket = io("ws://127.0.0.1:4385");
    const SAMPLE_RATE = 44100;
    let ctx = new AudioContext({ sampleRate: SAMPLE_RATE });
    // 音频数据队列,累计2个缓冲区再开始播放避免网络抖动导致断音
    let audioQueue = [];
    let isPlaying = false;

    // 监听服务端发来的音频流
    socket.on('audio-play', (audioBuffer) => {
        const pcmData = new Float32Array(audioBuffer);
        audioQueue.push(pcmData);
        if (audioQueue.length >= 2 && !isPlaying) {
            playBuffer();
        }
    });

    function playBuffer() {
        if (audioQueue.length === 0) {
            isPlaying = false;
            return;
        }
        isPlaying = true;
        const pcmData = audioQueue.shift();
        // 创建音频缓冲区写入PCM数据
        const buffer = ctx.createBuffer(1, pcmData.length, SAMPLE_RATE);
        buffer.copyToChannel(pcmData, 0);
        // 创建播放节点
        const source = ctx.createBufferSource();
        source.buffer = buffer;
        source.connect(ctx.destination);
        // 当前缓冲区播放完成后自动播放下一个
        source.onended = playBuffer;
        source.start();
    }
});

可优化方向

  • 延迟优化:将BUFFER_SIZE调低到2048/1024,可将端到端延迟控制在100ms以内
  • 带宽优化:发送端使用Opus编码压缩PCM数据,接收端解码后播放,可将数据量降低到原有的1/10左右
  • 性能优化:现代浏览器可将ScriptProcessorNode替换为AudioWorklet,避免主线程阻塞导致的音频卡顿

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 11:18:03