- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在尝试解决 previous issue 问题时临时创建了一个没有任何 iceServers
的 RTCPeerConnection
.
let peer = new RTCPeerConnection();
这在我的本地网络上一直运行良好。
但是,不在同一网络上的设备(例如,4G 手机)将无法连接。我记得我必须将一些 iceServers
添加回 RTCPeerConnection
构造函数。
let peer = RTCPeerConnection(
{
iceServers: [
{
urls: [
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
],
},
{
urls: [
"stun:global.stun.twilio.com:3478?transport=udp",
],
},
],
iceCandidatePoolSize: 10,
}
);
这样做之后,我的WebRTC连接就一直卡在连接状态。即使在我的本地网络上,也没有一个连接成功。 (不再是这种情况,请参阅下面的编辑2)
这是连接状态:
编辑:当我将 iceServers
留空时,连接仍然收集了一个 ice 候选者,所以我假设我的浏览器 (chrome) 提供了一个默认的 ice服务器。在那种情况下,只有我的自定义 ice 服务器(如上所示)导致了问题,而不是浏览器默认设置。
编辑 2:新观察
我已经添加了大量的日志记录,而且每当我包含 iceServers
时,我都会注意到一些事情:
每当对等点 A 在一段时间内第一次发起与对等点 B 的连接时,对等点 B 会收集两个 ice 候选者:1 个本地主机候选者和 1 个远程候选者。如上所述,连接失败。
但是当我快速尝试再次连接时......对等点 B 只收集了一个 ice 候选者:本地主机候选者。不聚集远程候选人。我的第一个假设是我正在使用的 STUN 服务器(在这种情况下很可能是谷歌的)对其服务有某种形式的速率限制。这个场景真正有趣的是,连接成功了!!
远程候选人搞乱连接有些神秘……我希望这些新细节能有所帮助。我被困在这几个月了!而且这两个设备都在我的局域网上,所以我希望远程候选人完全没有影响。
Peer A代码(发起者):
export class WebRTCConnection {
private _RTCPeerConnection: any;
private _fetch: any;
private _crypto: any;
private _entity: any;
private _hostAddress: any;
private _eventHandlers: ConnectionEventHandlers;
private _peer: any;
private _peerChannel: any;
constructor({
entity,
hostAddress,
eventHandlers,
RTCPeerConnection,
fetch,
crypto,
}: {
entity: any,
hostAddress: any,
eventHandlers: ConnectionEventHandlers,
RTCPeerConnection: any,
fetch: any,
crypto: any,
}) {
this._RTCPeerConnection = RTCPeerConnection;
this._fetch = fetch;
this._crypto = crypto;
this._entity = entity;
this._hostAddress = hostAddress;
this._eventHandlers = eventHandlers;
this._initPeer();
}
async _initPeer() {
this._peer = new this._RTCPeerConnection(/* as shown in question */);
let resolveOfferPromise: (value: any) => void;
let resolveIceCandidatesPromise: (value: any[]) => void;
let iceCandidatesPromise: Promise<any[]> = new Promise((resolve, _reject) => {
resolveIceCandidatesPromise = resolve;
});
let offerPromise: Promise<any> = new Promise((resolve, _reject) => {
resolveOfferPromise = resolve;
});
this._peer.onnegotiationneeded = async () => {
let offer = await this._peer.createOffer();
await this._peer.setLocalDescription(offer);
resolveOfferPromise(this._peer.localDescription);
};
this._peer.onicecandidateerror = () => {
// log error
};
let iceCandidates: any[] = [];
this._peer.onicecandidate = async (evt: any) => {
if (evt.candidate) {
// Save ice candidate
iceCandidates.push(evt.candidate);
} else {
resolveIceCandidatesPromise(iceCandidates);
}
};
(async () => {
// No more ice candidates, send on over signaling service
let offer: any = await offerPromise;
let iceCandidates: any[] = await iceCandidatesPromise;
let sigData = // reponse after sending offer and iceCandidates over signaling service
let answer = sigData.answer;
await this._peer.setRemoteDescription(answer);
for (let candidate of sigData.iceCandidates) {
await this._peer.addIceCandidate(candidate);
}
})();
this._peer.onicegatheringstatechange = (evt: any) => {
// log state
};
this._peer.onconnectionstatechange = async () => {
// log state
};
this._peerChannel = this._peer.createDataChannel("...", {
id: ...,
ordered: true,
});
this._peerChannel.onopen = () => {
// log this
};
this._peerChannel.onmessage = (event: any) => {
// do something
};
}
send(msg: any) {
this._peerChannel.send(
new TextEncoder().encode(JSON.stringify(msg)).buffer,
);
}
close() {
if (this._peer) {
this._peer.destroy();
}
}
}
对等 B 代码:
export class WebRTCConnection {
constructor({ signalData, eventHandlers, RTCPeerConnection }) {
this._eventHandlers = eventHandlers;
this._peer = new RTCPeerConnection(/* as seen above */);
this._isChannelOpen = false;
this._peer.ondatachannel = (event) => {
event.channel.onopen = () => {
this._mainDataChannel = event.channel;
event.channel.onmessage = async (event) => {
// do something
};
this._isChannelOpen = true;
};
};
this._peer.onicecandidateerror = () => {
// log error
};
this._iceCandidates = [];
this._isIceCandidatesFinished = false;
this._iceCandidatesPromise = new Promise((resolve, _reject) => {
this._resolveIceCandidatesPromise = resolve;
});
this._isAnswerFinished = false;
this._isSignalDataSent = false;
this._peer.onicecandidate = async (evt) => {
if (evt.candidate) {
// Save ice candidate
this._iceCandidates.push(evt.candidate);
} else {
// No more ice candidates, send on over signaling service when ready
this._isIceCandidatesFinished = true;
this._resolveIceCandidatesPromise();
this._sendSignalData();
}
};
(async () => {
let sigData = JSON.parse(signalData);
let offer = sigData.offer;
await this._peer.setRemoteDescription(offer);
this._answer = await this._peer.createAnswer();
await this._peer.setLocalDescription(this._answer);
for (let candidate of sigData.iceCandidates) {
await this._peer.addIceCandidate(candidate);
}
this._isAnswerFinished = true;
this._sendSignalData();
})();
this._peer.onconnectionstatechange = async () => {
// log state
};
}
_sendSignalData() {
if (false
|| !this._isIceCandidatesFinished
|| !this._isAnswerFinished
|| this._isSignalDataSent
) {
return;
}
this._isSignalDataSent = true;
this._eventHandlers.onSignal(JSON.stringify({
answer: {
type: this._answer.type,
sdp: this._answer.sdp,
},
iceCandidates: this._iceCandidates,
}));
}
send(msg) {
this._mainDataChannel.send(new TextEncoder().encode(JSON.stringify(msg)));
}
close() {
this._peer.destroy();
}
}
最佳答案
您的代码在没有 iceServers
的 LAN 上运行,因为 STUN 服务器不用于收集候选主机——您的计算机已经知道其本地 IP 地址——并且候选主机足以在 LAN 上建立 WebRTC 连接。
连接可能失败,因为其中一个对等方在 symmetric NAT 之后,其中 STUN无法工作。您可以使用此页面中的代码检查网络是否位于对称 NAT 后面:Am I behind a Symmetric NAT? (此页面还提供了一个 JSFiddle ,您可以在其中检查控制台消息是否打印“normal nat”或“symmetric nat”。如果它什么都不打印,而 fiddle 正常工作,则表示您没有获取服务器反身候选人。)
我认为你应该首先在 WAN 上测试你的代码,并与被检查是否在正常 nat 之后的同行一起测试你的代码。您是否曾经在 WAN 上尝试过通过以太网或 WiFi 连接的两个对等体的代码?似乎 3G/4G 网络通常处于对称 NAT 之下。
更新(感谢 @Sean DuBois 的 comment ):“symmetric NAT”,这是我在上面使用并在 RFC 3489 中引入的表达式(2003 年 3 月),可以用 RFC 4787 中引入的更新术语来更好地命名。 (2007 年 1 月)。 STUN仅适用于具有“端点独立映射”行为的 NAT。 “对称 NAT”(旧术语)具有“Address-Dependent Mapping”行为或“Address and Port-Dependent Mapping”行为,不是“端点独立映射”行为。
关于javascript - 当包含 ice 服务器时,WebRTC 卡在连接状态(远程候选甚至通过 LAN 引起问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62772851/
在 Android 的 API > 19 中是否有任何方法可以获取可移动 SD 卡的路径? 与外部 SD 卡一样,我们有 Environment.getExternalStorageDirectory
一些 Android 设备有 microSD(或其他存储卡)插槽,通常安装为 /storage/sdcard1 据我所知,自 Android 4.4 起 Google 限制了对此内存的访问,并在 An
我使用 Java Card 2.1.2 SDK 和 GPShell 作为与设备通信的方式在 Java Card 上构建一个项目。我从 GpShell 测试了 helloworld 示例,并成功发送了
我开发了一个应用程序,它有一个来电接收器,它适用于所有手机。一位用户有一部双 SIM 卡安卓手机。该应用程序适用于第一张 SIM 卡。但是当有人调用他的第二张 SIM 卡时,我们的应用程序不会被调用。
我有一个带预览的文件输入。 这是笔 Codepen 我想强制高度,我无法理解我该怎么做。我想将此组件的高度固定为 300px(示例),我还需要保持加载图像的正确纵横比,用灰色背景填充空白。现在我保持宽
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?将问题更新为 on-topic对于堆栈溢出。 6年前关闭。 Improve this qu
我正在使用此代码访问 SD card : import os from os.path import join from jnius import autoclass #from android.pe
我正在为数据记录设备编写固件。它以 20 Hz 的频率从传感器读取数据并将数据写入 SD 卡。但是,向SD卡写入数据的时间并不一致(大约200-300 ms)。因此,一种解决方案是以一致的速率将数据写
我正在使用以下代码将视频放到网站上,但是在垂直方向上,手机屏幕上只能看到视频的左半部分 我不是网络开发人员。有人可以告诉我确切的内容吗,如何使其正确放置在手机屏幕上? 是在youtube iframe
我正在使用 Vuetify 1.5 和 Vuetify 网格系统来设置我的布局。现在我有一个组件 HelloWorld我将其导入到我的 Parent 中成分。我已经在我的 HelloWorld 中设置
我使用 python 制作了一个简单的二十一点游戏。我制作了游戏的其余部分,但我正在努力放入 ASCII 卡,所以这只是代码的一小部分。我尝试将 * len(phand) 放在附加行的末尾。虽然这确实
我正在使用玩家卡设置 Twitter 卡。它可以在预览工具中运行,但文档说它需要在“twitter.com 现代桌面浏览器? native iOs 和 Android Twitter 应用程序?mob
任何旧的 GSM 兼容 SIM 卡(3G USIM 的奖励)。 我想我需要一些硬件?谁能为业余爱好者推荐一些便宜的东西,以及一些更专业的东西? 我认为会有一个带有硬件的 API 的完整文档,所以也许这
我使用 python 制作了一个简单的二十一点游戏。我制作了游戏的其余部分,但我正在努力放入 ASCII 卡,所以这只是代码的一小部分。我尝试将 * len(phand) 放在附加行的末尾。虽然这确实
我记得前一段时间读到有 cpu 卡供系统添加额外的处理能力来进行大规模并行化。任何人都有这方面的经验和任何资源来研究项目的硬件和软件方面吗?这项技术是否不如传统集群?它更注重功率吗? 最佳答案 有两个
我检查外部存储是否已安装并且可用于读/写,然后从中读取。我使用的是确切的官方 Android 示例代码 ( from here )。 它说外部存储未安装。 getExternalFilesDir(nu
在 Android 2.1 及更低版本中,Android 应用程序可以请求下载到 SD 卡上吗?另外我想知道应用程序是否可以请求一些包含视频的文件夹下载到 SD 卡上?以及如何做到这一点? 提前致谢。
我们编写了一个 Windows 设备驱动程序来访问我们的自定义 PCI 卡。驱动程序使用 CreateFile 获取卡的句柄。 我们最近在一次安装中遇到了问题,卡似乎停止工作了。我们尝试更换卡(更换似
有些新设备(例如 Samsung Galaxy)带有两个 SD 卡。我想知道是否有任何方法可以确定设备是否有两张 SD 卡或一张 SD 卡。谢谢 最佳答案 我认为唯一的方法是使用 检查可用根的列表 F
我正在尝试将文件读/写到 SD 卡。我已经尝试在我的真实手机和 Eclipse 中的模拟器上执行此操作。在这两种设备上,对/mnt/sdcard/或/sdcard 的权限仅为“d--------”,我
我是一名优秀的程序员,十分优秀!