1

我使用 NodeJS 和 WebRTC 创建了一个简单的点对点应用程序,用于一对多直播应用程序。

到目前为止,它正在我的本地主机上运行,​​但是当我将应用程序部署在 Google Cloud Platform 上的生产 VM 服务器上时,我无法使用peer.createDataChannel(). 或者至少这是我看到的问题,因为它没有抛出任何错误。

服务器.js

const port = process.env.PORT || 80;

const express = require('express');
const bodyParser = require('body-parser');
const webrtc = require('wrtc');

const app = express();

const status = {
    offline: 'offline',
    online: 'online',
    streaming: 'streaming'
};

let hostStream;
let hostChannel;
let channelData = {
    status: status.offline,
    message: null
};

app.use(express.static('public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/broadcast', async ({ body }, res) => {
    try {
        let peer = new webrtc.RTCPeerConnection({
            iceServers: [
                {
                    urls: "stun:stun.stunprotocol.org"
                }
            ]
        });

        peer.ontrack = (e) => handleTrackEvent(e, peer);
        peer.ondatachannel = (e) => handleHostDataChannelEvent(e);

        let desc = new webrtc.RTCSessionDescription(body.sdp);
        await peer.setRemoteDescription(desc);

        let answer = await peer.createAnswer();
        await peer.setLocalDescription(answer);

        let payload = {
            sdp: peer.localDescription,
            status: channelData.status
        };

        res.json(payload);
    } catch (e) {
        console.log(e);
    }
});

function handleTrackEvent(e, peer) {
    hostStream = e.streams[0];
}

function handleHostDataChannelEvent(e) {
    let channel = e.channel;

    channel.onopen = function(event) {
        channelData.message = '[ SERVER ]: Peer-to-peer data channel has been created.';
        channel.send(JSON.stringify(channelData));
        channelData.message = null;
    }
    channel.onmessage = function(event) {
        console.log(event.data);
    }

    hostChannel = channel;
}

app.listen(port, () => console.log('[ SERVER ]: Started'));

流光.js

function createPeer() {
    let peer = new RTCPeerConnection({
        iceServers: [
            {
                urls: "stun:stun.stunprotocol.org"
            }
        ]
    });

    let channel = peer.createDataChannel('host-server');
    channel.onopen = function(event) {
        channel.send('Host: Data Channel Opened');
    }
    channel.onmessage = function(event) {
        let data = JSON.parse(event.data);

        if('status' in data) {
            $('body').removeClass().addClass(data.status);
        }

        if('message' in data && data.message != null) {
            $.toast({
                heading: 'Data Channel',
                text: data.message,
                showHideTransition: 'slide',
                icon: 'info',
                position: 'top-center',
                stack: false
            })
        }
    }

    peer.onnegotiationneeded = () => handleNegotiationNeededEvent(peer);

    return peer;
}

在我的本地主机上,当主机 ( streamer.js) 启动流媒体时,服务器Host: Data Channel Opened在控制台和主机的浏览器上输出,我看到了带有消息的 toast Server: Peer-to-peer data channel has been created.。但是,当我在生产服务器上尝试应用程序时,服务器不会在控制台上记录该应用程序,并且主机的浏览器也不会打开 toast 并显示数据通道已创建的消息。

浏览器控制台和服务器控制台都没有错误,所以我真的不知道问题出在哪里。

4

1 回答 1

1

我没有在您的代码中看到冰候选人的聚集 - 因此您的同行无法相互建立联系也就不足为奇了。这是您的代码应该是什么样子的工作示例。

流光.js:

async function createPeer(configuration) {
  const localCandidates = [];
  // Step 1. Create new RTCPeerConnection
  const peer = new RTCPeerConnection(configuration);
  peer.onconnectionstatechange = (event) => {
    console.log('Connection state:', peer.connectionState);
  };
  peer.onsignalingstatechange = (event) => {
    console.log('Signaling state:', peer.signalingState);
  };
  peer.oniceconnectionstatechange = (event) => {
    console.log('ICE connection state:', peer.iceConnectionState);
  };
  peer.onicegatheringstatechange = (event) => {
    console.log('ICE gathering state:', peer.iceGatheringState);
  };
  // Step 5. Gathering local ICE candidates
  peer.onicecandidate = async (event) => {
    if (event.candidate) {
      localCandidates.push(event.candidate);
      return;
    }
    // Step 6. Send Offer and client candidates to server
    const response = await fetch('/broadcast', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        offer: offer,
        candidates: localCandidates,
      }),
    });
    const {answer, candidates} = await response.json();
    // Step 7. Set remote description with Answer from server
    await peer.setRemoteDescription(answer);
    // Step 8. Add ICE candidates from server
    for (let candidate of candidates) {
      await peer.addIceCandidate(candidate);
    }
  };
  // Step 2. Create new Data channel
  const dataChannel = peer.createDataChannel('host-server');
  dataChannel.onopen = (event) => {
    dataChannel.send('Hello from client!');
  };
  dataChannel.onclose = (event) => {
    console.log('Data channel closed');
  };
  dataChannel.onmessage = (event) => {
    console.log('Data channel message:', event.data);
  };
  // Step 3. Create Offer
  const offer = await peer.createOffer();
  // Step 4. Set local description with Offer from step 3
  await peer.setLocalDescription(offer);
  return peer;
}

const configuration = {
  iceServers: [
    {
      urls: 'stun:global.stun.twilio.com:3478?transport=udp',
    },
  ],
};
// Add turn server to `configuration.iceServers` if needed.
// See more at https://www.twilio.com/docs/stun-turn

createPeer(configuration);

server.js:

const express = require('express');
const bodyParser = require('body-parser');
const webrtc = require('wrtc');

const port = process.env.PORT || 80;
const configuration = {
  iceServers: [
    {
      urls: 'stun:global.stun.twilio.com:3478?transport=udp',
    },
  ],
};
// Add turn server to `configuration.iceServers` if needed.

const app = express();
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.post('/broadcast', async (req, res) => {
  const {offer, candidates} = req.body;
  const localCandidates = [];
  let dataChannel;
  // Step 1. Create new RTCPeerConnection
  const peer = new webrtc.RTCPeerConnection(configuration);
  peer.ondatachannel = (event) => {
    dataChannel = event.channel;
    dataChannel.onopen = (event) => {
      dataChannel.send('Hello from server!');
    };
    dataChannel.onclose = (event) => {
      console.log('Data channel closed');
    };
    dataChannel.onmessage = (event) => {
      console.log('Data channel message:', event.data);
    };
  };
  peer.onconnectionstatechange = (event) => {
    console.log('Connection state:', peer.connectionState);
  };
  peer.onsignalingstatechange = (event) => {
    console.log('Signaling state:', peer.signalingState);
  };
  peer.oniceconnectionstatechange = (event) => {
    console.log('ICE connection state:', peer.iceConnectionState);
  };
  peer.onicegatheringstatechange = (event) => {
    console.log('ICE gathering state:', peer.iceGatheringState);
  };
  peer.onicecandidate = (event) => {
    // Step 6. Gathering local ICE candidates
    if (event.candidate) {
      localCandidates.push(event.candidate);
      return;
    }
    // Step 7. Response with Answer and server candidates
    let payload = {
      answer: peer.localDescription,
      candidates: localCandidates,
    };
    res.json(payload);
  };
  // Step 2. Set remote description with Offer from client
  await peer.setRemoteDescription(offer);
  // Step 3. Create Answer
  let answer = await peer.createAnswer();
  // Step 4. Set local description with Answer from step 3
  await peer.setLocalDescription(answer);
  // Step 5. Add ICE candidates from client
  for (let candidate of candidates) {
    await peer.addIceCandidate(candidate);
  }
});

app.listen(port, () => console.log('Server started on port ' + port));

我发现你的 stun 服务器功能不全,所以我用 Twillio 的另一台替换了它。此外,我添加了事件处理程序,使用它可以轻松跟踪 WebRTC 会话的状态。你会很好地了解更多关于WebRTC 连接流的信息,真的。

于 2022-01-04T18:38:29.640 回答