想象一下,阿蓝和阿红想面对面聊天,但不想下载任何会议软件。他们只需要打开浏览器:阿蓝先把自己这扇“窗户”上的摄像头和麦克风打开(getUserMedia),然后托一个信使给对方递纸条,约定彼此的系统参数(信令交换)。纸条传完之后,两个人之间会努力搭出一条“跨楼专用走廊”(RTCPeerConnection),走廊一旦建成,画面和声音就在这条走廊里实时流动。 这条走廊并不是从头到尾都经过一个总机房,而是尽量点对点直达;只有在“门卫”不让走的时候,才借道小区之间的中转站。WebRTC 把这些复杂过程都藏进了浏览器,前端工程师只需要调用几个核心 API,就能在网页里实现视频会议、远程协作、AI 实时互动等能力。对 AI 从业者来说,它是把摄像头、麦克风变成“实时数据入口”的钥匙。

岗位关联

学懂 getUserMedia 与 RTCPeerConnection,等于同时掌握实时音视频的“取水阀”和“跨楼专线”,这是给任何 AI 应用加上实时交互能力的基础。

目录
  1. 可视化演示
  2. 整体架构
  3. 媒体采集:getUserMedia
  4. 采集约束
  5. 信令交换
  6. P2P连接:RTCPeerConnection
  7. ICE协商:穿透 NAT 的路由
  8. 媒体收发:轨道、编解码与统计
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示getUserMedia媒体流获取、RTCPeerConnection的P2P连接与SDP交换、ICE/STUN/TURN NAT穿透协商及MediaStream+AI实时推理管线。

本节配有交互动效(进入视口后自动加载,约 3KB)。

媒体获取:getUserMedia采集音视频 通过Constraints请求摄像头和麦克风权限,返回MediaStream MediaStream包含视频轨(VID)和音频轨(AUD)两条独立轨道 stream = await getUserMedia({video, audio}) getUserMedia是WebRTC媒体采集的入口API

整体架构

阿蓝和阿红想开一场视频见面会,首先要弄清楚家里需要准备什么。阿蓝不需要自己发明电话线,只需要三样东西:一个能把摄像头和话筒“接进窗边”的水龙头(getUserMedia),一条能跨越两栋楼的专线(RTCPeerConnection),以及一位在楼与楼之间传话的信使(信令)。

WebRTC 就是浏览器内建的这套“实时通信工具箱”。它不需要安装插件,也不依赖某个大公司的中心服务器做媒体转发,而是尽量让两个浏览器直接建立点对点连接。整体上,WebRTC 的 API 分工很清晰:getUserMedia 负责媒体采集,RTCPeerConnection 负责连接与传输,信令则没有指定具体协议,通常用 WebSocket 传递。

实时通信的每一毫秒都有价值,整个环节可以看成一个接力赛:

理解了这五段接力,再看 WebRTC 的两个核心 API 就会很清楚:getUserMedia 负责第一段,RTCPeerConnection 负责中间三段。阿蓝的“窗户、纸条、专线”故事,就这么串起来了。

端到端时延=t采集+t编码+t网络+t解码+t渲染\displaystyle \text{端到端时延} = t_{\text{采集}} + t_{\text{编码}} + t_{\text{网络}} + t_{\text{解码}} + t_{\text{渲染}}
这个公式把“一张脸从阿蓝的摄像头跑到阿红屏幕”的全程拆成五段,每一段都有延迟,AI 实时应用要优化的就是这五个环节。
  1. 采集耗时摄像头把光信号变成数字帧需要时间。
    t采集t_{\text{采集}}
  2. 编码耗时浏览器压缩原始视频,减少跨楼传送的体积。
    t编码t_{\text{编码}}
  3. 网络、解码与渲染压缩后的数据穿过专线,对端解压并画到屏幕上。
    t网络+t解码+t渲染t_{\text{网络}} + t_{\text{解码}} + t_{\text{渲染}}

媒体采集:getUserMedia

阿蓝要开始聊天,第一步不是打电话,而是先确认自己的摄像头和麦克风能工作。getUserMedia 就是这样一把“窗户钥匙”,它让浏览器打开设备,并把设备吐出的数据包装成一个叫 MediaStream 的“水流”。

调用它会触发浏览器的权限询问,只有在 HTTPS 或 localhost 页面下才能使用。拿到 MediaStream 后,你可以把它交给 <video> 元素做本地预览,也可以把里面的轨道交给 RTCPeerConnection。它不是一个 mp4 文件,而是一股不断更新的实时流,就像水龙头里持续流出的水,而不是一个装满水的水桶。

原始媒体流的体积非常大,这也是为什么 WebRTC 必须立刻进行编码压缩:

所以 1280×720 的未压缩一帧约 276 万字节。不压缩直接传,网络很快就会堵住;这就是为什么后续的编解码、码率控制如此重要。

一帧原始数据量=W×H×3\displaystyle \text{一帧原始数据量} = W \times H \times 3
这个公式算出未压缩的一帧有多大。W 和 H 是画面宽高,3 代表红绿蓝三个颜色通道。
  1. 宽度像素画面横向有多少个像素点。
    WW
  2. 高度像素画面纵向有多少个像素点。
    HH
  3. 颜色通道每个像素需要红、绿、蓝三个数值描述。
    ×3\times 3
js1 行
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });

采集约束

阿蓝开好窗户后,还可以调整摄像头的角度、分辨率和声音细节。getUserMedia 允许你传入一组“约束条件”,相当于告诉设备:我想要多清晰的画面、用前置还是后置摄像头、要多高的帧率。

ideal 表示“尽量满足但不强制”,浏览器会选一个最接近的值;还有 exact 表示“必须满足”,不满足就报错。facingMode: 'user' 表示前置摄像头,'environment' 表示后置摄像头。拿到轨道后,还能用 track.enabled = false 实现“静音/关画”,用 track.stop() 彻底关闭设备。

约束条件决定的是采集源头,直接影响后续码率:

实际开发中,你不应该直接让用户设置 4K 超高清,而要结合网络带宽动态调整分辨率。阿蓝的窗户再大,如果连接走廊太窄,还是需要主动把“水量”调小。

编码码率≈W×H×3×f×c\displaystyle \text{编码码率} \approx W \times H \times 3 \times f \times c
其中 f 是帧率,c 是压缩比。c 通常远小于 1,所以编码后码率才能从每秒几百 MB 降到几 Mbps。
  1. 画面面积宽高相乘得到每帧像素总数。
    W×HW \times H
  2. 颜色与帧率每个像素 3 字节,再乘每秒帧数,得到未压缩码率。
    ×3×f\times 3 \times f
  3. 压缩比编码器把没有变化的背景和冗余信息去掉,c 表示压缩后的比例。
    ×c\times c
js9 行
const stream = await navigator.mediaDevices.getUserMedia({  video: {    width: { ideal: 1280 },    height: { ideal: 720 },    frameRate: { ideal: 30 },    facingMode: 'user'  },  audio: { echoCancellation: true }});

信令交换

阿蓝知道自己的窗户长什么样,阿红也知道自己的,但彼此还不知道对方的存在。这时需要一位信使:把阿蓝的“我会说哪些编解码器、我的 IP 和端口是什么”写在纸条上,传给阿红;阿红看完后写一张回条。

这张纸条在 WebRTC 里叫 SDP(Session Description Protocol),这条消息交换过程称为信令。信令服务器通常只是转发纸条,不负责传输真正的音视频。WebRTC 没有规定信令必须用哪种技术,常见选择是 WebSocket、Socket.IO 甚至 HTTP 轮询。关键是必须有一个“双方都能找到的确信中转点”。

整个交换遵循 Offer/Answer 模型:

阿蓝把纸条交给信使,阿红收到后写下 Answer,双方都拿到对方的 SDP,才算完成“约见”的第一步。没有信令,后面的一切都无从谈起。

SDP={媒体类型,编解码器,IP/端口,安全指纹}\displaystyle SDP = \{ \text{媒体类型}, \text{编解码器}, \text{IP/端口}, \text{安全指纹} \}
SDP 不包含画面本身,只描述“我这边能用什么、该往哪里传”,就像纸条上写着房间号,而不是把整个房间搬过去。
  1. 媒体类型这张纸条是视频、音频还是数据通道。
    媒体类型\text{媒体类型}
  2. 编解码器我能用 VP8 还是 H.264,你用哪个。
    编解码器\text{编解码器}
  3. 地址与安全我的收件地址端口,以及用于加密的指纹。
    IP/端口,安全指纹\text{IP/端口}, \text{安全指纹}

P2P连接:RTCPeerConnection

纸条交换完成后,阿蓝需要找一个“总接口”来实际搭建跨楼专线,这个总接口就是 RTCPeerConnection。

创建连接后,先要调用 createOffer() 生成 Offer,再调用 setLocalDescription() 把本地描述设置好;把 Offer 通过信令发给对端后,对端调用 setRemoteDescription() 写入,并返回 Answer。双方都完成描述交换后,RTCPeerConnection 会收到对方发来的媒体流,触发 ontrack 事件。

RTCPeerConnection 不是一个瞬间完成的对象,而是一个有生命周期的状态机:

真实项目中,要监听 connectionState 的变化,在 failed 时重新创建连接或提示用户。阿蓝不会觉得“管道公司来了就等于走廊建好了”,她要看管道工人最终是否交货。

PC状态∈{new,connecting,connected,disconnected,failed,closed}\displaystyle \text{PC状态} \in \{ \text{new}, \text{connecting}, \text{connected}, \text{disconnected}, \text{failed}, \text{closed} \}
这个公式提醒你:创建对象不等于连接成功,必须监控状态,并做好断线重连。
  1. 初始与协商中new 表示刚创建,connecting 表示正在寻找通路。
    new,connecting\text{new}, \text{connecting}
  2. 已连接与退化connected 表示正常通话,disconnected 表示短暂失去联系。
    connected,disconnected\text{connected}, \text{disconnected}
  3. 失败与关闭failed 表示协商失败,closed 表示连接被主动关闭。
    failed,closed\text{failed}, \text{closed}
js4 行
const pc = new RTCPeerConnection({  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]});stream.getTracks().forEach(track => pc.addTrack(track, stream));

ICE协商:穿透 NAT 的路由

阿蓝和阿红都住在小区里,对外只有一个小区门卫。阿蓝写在纸条上的地址可能是内网地址,阿红直接访问这个地址是找不到人的。ICE 就是那套“找到一条真正能走通的路”的机制。

ICE 会收集三类候选地址:host 是本机网卡地址;srflx 是向 STUN 服务器询问后得到的“公网反射地址”;relay 是 TURN 服务器提供的中继地址。收集完成后,两端通过信令交换候选,然后两两配对做连通性检查,挑一条延迟最低的路径。

如果双方都在对称型 NAT 后面,STUN 反射地址也常常无法互通,这时只能走 TURN 中继。ICE 候选配对数量可以这样估算:

实际开发中,通常使用“trickle ICE”边发现边发送候选,而不是等全部收集完再交换。阿蓝和阿红一边试试这个门,一边试试那个窗,最终找到一条双方都能走通的走廊。

候选对总数=∣本地候选∣×∣远端候选∣\displaystyle \text{候选对总数} = |\text{本地候选}| \times |\text{远端候选}|
候选数越多,待检查的路线越多,连接成功概率越高,但耗时也会增加。
  1. 本地候选数阿蓝这边可用的路径数量。
    ∣本地候选∣|\text{本地候选}|
  2. 远端候选数阿红那边可用的路径数量。
    ∣远端候选∣|\text{远端候选}|
  3. 笛卡尔积配对每条本地路径都尝试搭配每条远端路径。
    ∣本地候选∣×∣远端候选∣|\text{本地候选}| \times |\text{远端候选}|

媒体收发:轨道、编解码与统计

走廊建好后,真正的工作才刚刚开始:画面和声音要持续在这条专线上流动。RTCPeerConnection 里的媒体以轨道(track)为单位,addTrack() 加入本地轨道,ontrack 收到远端轨道。

如果想要临时关闭视频但不断开连接,可以把 track.enabled 设为 false;如果想把摄像头从前置换成后置,可以用 replaceTrack()。同一套连接里还可以打开 RTCDataChannel,用来传文本、文件或 AI 分析结果,相当于在走廊里加了一条独立的“数据小管道”。

WebRTC 默认使用 VP8、VP9、H.264 等视频编码器和 Opus 音频编码器,并会根据网络情况自动调整码率:

AI 应用还可以调用 pc.getStats() 拿到实时丢包、帧率、码率等指标,用来做网络质量监控或自动降清晰度。阿蓝的走廊已经建成,剩下的事情就是让水、电、数据在走廊里稳定地流动,并随时监控每段路况。

目标码率=min⁡(带宽估计,最高码率)\displaystyle \text{目标码率} = \min(\text{带宽估计}, \text{最高码率})
浏览器实时估测链路带宽,然后选择“既不卡顿、又尽量清晰”的码率,避免把走廊堵死。
  1. 带宽估计浏览器通过丢包率、延迟等指标估算当前可用带宽。
    带宽估计\text{带宽估计}
  2. 最高码率采集约束中允许的上限。
    最高码率\text{最高码率}
  3. 取最小值取两者中更小的值作为编码目标,保证不超出链路能力。
    min⁡(带宽估计,最高码率)\min(\text{带宽估计}, \text{最高码率})

AI 应用场景

AI 虚拟面试官

阶段:实时媒体采集与传输

解决的问题:传统视频面试需要安装客户端,且 AI 能力难以嵌入音视频链路。用 getUserMedia 在浏览器内直接采集考生画面,再通过 RTCPeerConnection 将画面实时传给远程考官;浏览器端 AI 同时分析眼神、语速和表情,并把结果通过 RTCDataChannel 发送给面试系统。

具体案例:一家在线教育公司要上线“AI 模拟面试”,候选人打开网页即可面试。前端用 getUserMedia 获取摄像头流,RTCPeerConnection 负责与考官端建连,AI 打分结果实时显示在考官面板上,全程无需下载插件。

实时 AI 字幕会议

阶段:媒体处理与数据通道

解决的问题:视频会议中语音识别服务需要拿到音频流,如果先上传到中心服务器再转发,延迟高且带宽成本大。用 getUserMedia 采集本地音频,经 RTCPeerConnection 点对点传给参与者;其中一端用 Web Audio 和 ASR 模型实时转写,字幕通过 RTCDataChannel 分发。

具体案例:企业内部会议工具增加“实时字幕”功能,发言人的声音在浏览器端被 AI 识别成文字,并通过同一条 P2P 连接的数据通道同步到所有参会者屏幕,延迟低于 1 秒。

AI 远程医疗会诊

阶段:音视频流与 AI 辅助诊断

解决的问题:远程医疗需要高清、低延迟的画面,同时 AI 还要对影像做实时分析。通过采集约束设置 1080p 分辨率,用 RTCPeerConnection 建立 P2P 通道;接收端用 getStats 监控网络质量,并在画面中叠加 AI 标注框。

具体案例:一家远程医疗平台让基层医生用浏览器连接专家端,AI 在专家端自动标记皮肤影像的疑似病灶,并通过 WebRTC 把标注后的画面实时回传,帮助专家快速判断。

Python 演示

python71 行
# 信令交换模拟器: 演示 WebRTC 建立连接前 A/B 交换 SDP 和 ICE 候选class Message:    def __init__(self, sender, receiver, payload):        self.sender = sender        self.receiver = receiver        self.payload = payload class SignalingChannel:    def __init__(self):        self.messages = []    def send(self, sender, receiver, payload):        self.messages.append(Message(sender, receiver, payload))    def relay_all(self):        for msg in self.messages:            print("{0} -> {1}: {2}".format(msg.sender, msg.receiver, msg.payload)) class PeerSim:    def __init__(self, name):        self.name = name        self.local_sdp = ""        self.remote_sdp = ""        self.candidates = []        self.remote_candidates = []     def create_offer(self):        self.local_sdp = self.name + " offer(video=vp8,audio=opus)"        return self.local_sdp     def create_answer(self, remote_sdp):        self.remote_sdp = remote_sdp        self.local_sdp = self.name + " answer(video=vp8,audio=opus)"        return self.local_sdp     def set_remote_description(self, remote_sdp):        self.remote_sdp = remote_sdp     def add_candidate(self, candidate):        self.candidates.append(candidate)     def receive_candidate(self, candidate):        self.remote_candidates.append(candidate)     def connected(self):        return bool(self.local_sdp and self.remote_sdp and                    self.candidates and self.remote_candidates) def main():    channel = SignalingChannel()    alice = PeerSim("alice")    bob = PeerSim("bob")     offer = alice.create_offer()    channel.send(alice.name, bob.name, "SDP:" + offer)    bob.set_remote_description(offer)     answer = bob.create_answer(offer)    channel.send(bob.name, alice.name, "SDP:" + answer)    alice.set_remote_description(answer)     alice.add_candidate("host:192.168.1.10")    bob.add_candidate("srflx:203.0.113.5:3478")    channel.send(alice.name, bob.name, "candidate:" + alice.candidates[-1])    channel.send(bob.name, alice.name, "candidate:" + bob.candidates[-1])    alice.receive_candidate(bob.candidates[-1])    bob.receive_candidate(alice.candidates[-1])     channel.relay_all()    print("P2P-ready:", alice.connected(), bob.connected()) if __name__ == "__main__":    main()
目的:用 Python 模拟 WebRTC 建连前的信令流程,让初学者理解 SDP Offer/Answer 与 ICE 候选如何在两端之间交换。
  1. class SignalingChannel:模拟“信使”的中转站,所有消息都先经过它。
  2. channel.send(alice.name, bob.name, "SDP:" + offer)把阿蓝生成的 Offer 纸条发给阿红。
  3. bob.set_remote_description(offer)阿红写入阿蓝的 SDP,知道自己该用什么参数回应。
  4. answer = bob.create_answer(offer)阿红生成 Answer 纸条。
  5. alice.set_remote_description(answer)阿蓝收到 Answer 后,双方 SDP 协商完成。
  6. alice.add_candidate("host:192.168.1.10")阿蓝添加一条本地 ICE 候选地址。
  7. alice.receive_candidate(bob.candidates[-1])阿蓝收到阿红的候选地址,等待连通性检查。
  8. print("P2P-ready:", alice.connected(), bob.connected())双方都具备 SDP 和候选时,模拟连接可以进入就绪状态。

常见误区

误区WebRTC 是 P2P 的,所以建连后不需要任何服务器,连建连前也不需要。

正解P2P 指的是媒体流尽量点对点,不代表零服务器。建立连接前必须有一个信令服务器传递 SDP 和 ICE 候选;在对称型 NAT 下,还需要 TURN 服务器中继媒体。阿蓝和阿红的纸条总得有人递,特殊情况下走廊也得借别人的中转站。

为何常见:看到“P2P”就理解为“完全对等、无中心”,忽略了信令和 NAT 穿透这两个必须依赖服务器的环节。

误区调用 getUserMedia 成功后,会得到一个 mp4 文件,可以保存起来或直接给视频软件播放。

正解它返回的是实时 MediaStream,不是文件。MediaStream 是持续流动的数据源,要显示就把它赋给 <video> 元素的 srcObject;要录成文件必须再用 MediaRecorder 编码存储。它像水龙头里的活水,不像水桶里的死水。

为何常见:很多媒体 API 返回 Blob 或文件 URL,容易让人把实时流也想象成静态文件。

误区只要 `new RTCPeerConnection()` 并 addTrack,浏览器就能自动通话。

正解构造函数只是创建“总接口”,真正连通需要完成 SDP Offer/Answer 和 ICE 候选交换。A 发 Offer,B 回 Answer,双方 setRemoteDescription,再交换候选,最后状态才会变成 connected。相当于只约来了管道公司,还没递纸条确认见面地点。

为何常见:把“创建对象”和“建立连接”混为一谈,忽略了连接状态机。

误区两端一直连不上,一定是宽带不够或 Wi-Fi 信号太差。

正解连不上更可能是“地址不可达”而不是“带宽不足”。当双方都在对称型 NAT 后时,STUN 反射地址无法互通,即使带宽很好也会失败,只能靠 TURN 中继。阿蓝阿红如果都被门卫严格拦截,只有借小区中转货梯才能汇合。

为何常见:把网络质量与网络连通性混为一谈,前者看带宽和延迟,后者看地址能否互相访问。

误区既然是浏览器原生 API,视频应该裸传,不需要编码解码。

正解裸视频数据量太大,1280×720 一帧就约 2.7MB,每秒 30 帧会超过 800MB,根本无法实时传输。浏览器会自动协商 VP8/H.264 视频编码和 Opus 音频编码,并在弱网时降码率。专线里的水必须经过压缩罐才能高效运送。

为何常见:浏览器把编解码藏得太深,让人误以为“原生”就等于“没有编解码”。

面试题

Q1. getUserMedia 的主要作用是什么?

单选题 · easy

  • A.获取摄像头和麦克风流← 正确答案
  • B.建立浏览器间P2P连接
  • C.编码压缩音视频数据
  • D.传输信令消息给对端

答案:A。getUserMedia 只负责“打开设备取流”,相当于阿蓝把摄像头和话筒接到窗边;P2P 连接由 RTCPeerConnection 负责,编码和信令也不是它的职责。

Q2. WebRTC 中负责协商媒体参数的 SDP 消息由哪两个环节构成?

单选题 · mid

  • A.Offer 与 Answer← 正确答案
  • B.Play 与 Pause
  • C.Pull 与 Push
  • D.Host 与 Relay

答案:A。SDP 协商像递两张纸条:A 写 Offer,B 回 Answer,双方 setRemoteDescription 后才知道对方用什么编解码器、收在哪个地址。Play/Pause 是播放控制,Pull/Push 是其他协议习惯,Host/Relay 是 ICE 候选类型。

Q3. 让 RTCPeerConnection 发送本地音视频流的正确调用是?

单选题 · mid

  • A.addTrack() 添加媒体轨道← 正确答案
  • B.connect() 建立网络连接
  • C.open() 打开媒体设备
  • D.init() 初始化传输配置

答案:A。pc.addTrack(track, stream) 把本地轨道交给连接对象,之后才参与 SDP 协商和媒体发送;connect/open/init 都不是 RTCPeerConnection 的标准取流方式。

Q4. 对称型 NAT 下,WebRTC 最可靠的连通方案是?

单选题 · hard

  • A.依赖 TURN 中继转发媒体← 正确答案
  • B.增大 STUN 探测请求频率
  • C.手动指定固定公网 IP
  • D.关闭浏览器的安全策略

答案:A。对称型 NAT 的映射每次目标不同,普通 STUN 反射地址无法互通,只能通过 TURN 提供中继候选;加大探测频率和关闭安全策略都解决不了地址可达性问题。

Q5. ICE 候选地址的主要作用是?

单选题 · easy

  • A.提供一条可尝试的连通路径← 正确答案
  • B.保存聊天记录到云端
  • C.管理摄像头的硬件驱动
  • D.加密用户的登录会话口令

答案:A。ICE 候选就是阿蓝和阿红可能走通的一条条路径,包括本机地址、STUN 反射地址和 TURN 中继地址,目的是找到一条真正能连通的路。

Q6. 信令服务器在 WebRTC 中的正确角色是?

单选题 · mid

  • A.建立阶段传递 SDP 与候选← 正确答案
  • B.持续转发所有媒体数据
  • C.代替浏览器完成视频编码
  • D.存储并回放音视频流

答案:A。信令服务器只在建连阶段传递纸条;真正的音视频默认走 P2P 专线。只有在使用 TURN 中继时,服务器才转发媒体,但这和信令是两回事。

Q7. 要把本地摄像头画面送给对端,下面哪些做法正确?

多选题 · mid

  • A.getUserMedia 拿到 MediaStream← 正确答案
  • B.addTrack 把媒体轨道加入连接← 正确答案
  • C.video.srcObject 先做本地预览← 正确答案
  • D.MediaStream.createOffer 发起协商

答案:ABC。正确流程是先 getUserMedia 取流,再用 addTrack 加入 RTCPeerConnection,并可用 srcObject 本地预览;createOffer 是 RTCPeerConnection 的方法,MediaStream 上没有这个方法。

Q8. 关于 RTCPeerConnection 的正确说法有?

多选题 · hard

  • A.既能传音视频也能传数据← 正确答案
  • B.建连前需要交换 SDP 描述← 正确答案
  • C.可以绕过浏览器安全策略
  • D.连接后无需任何服务器

答案:AB。RTCPeerConnection 既能传音视频轨道,也能通过 RTCDataChannel 传数据;建连前必须交换 SDP 和 ICE 候选。它不能绕过安全策略,连接后也可能依赖 TURN 中继服务器。

Q9. WebRTC 中 ICE 候选包含哪些标准类型?

多选题 · hard

  • A.host 本地内网地址候选← 正确答案
  • B.srflx STUN 反射地址候选← 正确答案
  • C.relay TURN 中继转发候选← 正确答案
  • D.proxy 网关代理转发候选

答案:ABC。host、srflx、relay 是 WebRTC 三类标准 ICE 候选;proxy 不是标准类型。就像阿蓝的路径可以是自家门牌、门卫报出的对外门牌,或小区中转货梯。

Q10. 用户处于对称型 NAT 后,始终无法与对方建立 P2P,最可能原因是?

单选题 · extreme

  • A.信令交换内容不匹配
  • B.浏览器版本太过陈旧
  • C.缺少 TURN 中继服务← 正确答案
  • D.对方摄像头权限被拒

答案:C。对称型 NAT 需要 TURN 中继这一“小区中转货梯”;信令不匹配、浏览器陈旧、摄像头权限被拒会引发各自问题,但不是对称 NAT 场景下的首因。