WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试
WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试
WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试
- WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试
- 前言
- 一、TURN协议
- 1、连接Turn Server 流程
- ① 抓包交换流程图
- ② TURN协议格式解析
- 协议基础与消息结构
- 关键方法类型
- 核心属性类型
- 错误码与状态
- 数据传输机制
- ③ 发送Allocate Request UDP 信息包信息
- 再次发送 Allocate Request UDP 包 带上 协议、用户名和sha-1(user:realm:paasword)签名
- ④ 发送CreatePermission Request
- 发送数据Send Indication
- 接收 Data Indiaction
- ⑤ Channel-Bind Request
- 发送数据 ChannelData Message
- 2、 TURN测试Demo
- 二 、 iceCandidate收集的时机有两种
- 1、创建PeerConnection对象传入的参数ice_candidate_pool_size大于0会创建连接池的个数, 如果没有不会创建连接池ICE的速度就会有点慢
- 2、 设置sdp时获取 iceCandidate
- 总结
前言
TURN协议:文档RFC5766
文档中TURN协议工作原理图
Peer AServer-Reflexive +---------+Transport Address | |192.0.2.150:32102 | || /| |TURN | / ^| Peer A |Client's Server | / || |Host Transport Transport | // || |Address Address | // |+---------+10.1.1.2:49721 192.0.2.15:3478 |+-+ // Peer A| | ||N| / Host Transport| +-+ | ||A|/ Address| | | | v|T| 192.168.100.2:49582| | | | /+-++---------+| | | |+---------+ / +---------+| || |N| || | // | || TURN |v | | v| TURN |/ | || Client |----|A|----------| Server |------------------| Peer B || | | |^ | |^ ^| || | |T|| | || || |+---------+ | || +---------+| |+---------+| || | || || | |+-+| | || | || | |Client's | Peer BServer-Reflexive Relayed TransportTransport Address Transport Address Address192.0.2.1:7000 192.0.2.15:50000 192.0.2.210:49191
WebRTC中走中继Relay 调用流程图

一、TURN协议
TURN TURN Peer Peerclient server A B|-- Allocate request --------------->| | || | | ||<--------------- Allocate failure --| | || (401 Unauthorized) | | || | | ||-- Allocate request --------------->| | || | | ||<---------- Allocate success resp --| | || (192.0.2.15:50000) | | |// // // //| | | ||-- Refresh request ---------------->| | || | | ||<----------- Refresh success resp --| | || | | |
TURN TURN Peer Peerclient server A B| | | ||-- CreatePermission req (Peer A) -->| | ||<-- CreatePermission success resp --| | || | | ||--- Send ind (Peer A)-------------->| | || |=== data ===>| || | | || |<== data ====| ||<-------------- Data ind (Peer A) --| | || | | || | | ||--- Send ind (Peer B)-------------->| | || | dropped | || | | || |<== data ==================|| dropped | | || | | |
--------------------------------------------------------------------------------------
TURN TURN Peer Peerclient server A B| | | ||-- ChannelBind req ---------------->| | || (Peer A to 0x4001) | | || | | ||<---------- ChannelBind succ resp --| | || | | ||-- [0x4001] data ------------------>| | || |=== data ===>| || | | || |<== data ====| ||<------------------ [0x4001] data --| | || | | ||--- Send ind (Peer A)-------------->| | || |=== data ===>| || | | || |<== data ====| ||<------------------ [0x4001] data --| | || | | |Figure 4
1、连接Turn Server 流程
和国标gb28181的协议差不多 都是先发送一包AllocateRequest到服务返回一个错误码然后在发送一个AllocateRequest请求带上 用户名和密码 然后Trunserver返回映射turn上ip地址和port端口

① 抓包交换流程图

② TURN协议格式解析
协议基础与消息结构
TURN协议基于STUN协议扩展,采用STUN消息格式(头部+属性列表),同时新增了专用于中继功能的方法和属性
- 消息头(Header):
-
Class:固定为0x00(请求)或0x10(指示消息)
-
Method:定义操作类型(如Allocate、ChannelBind等)
-
Length:消息总长度(不含头部)
-
Transaction ID:16字节唯一标识符,用于匹配请求与响应
2. 属性列表(Attribute):
-
每个属性由Type(2字节)、Length(2字节)和Value(可变长)组成
-
例如:XOR-RELAYED-ADDRESS表示中继地址,CHANNEL-NUMBER标识信道号
关键方法类型
TURN协议新增以下STUN方法
- Allocate:客户端向服务器申请中继地址,响应中包含XOR-RELAYED-ADDRESS属性
- Refresh:延长中继地址的生命周期(通过LIFETIME属性设置超时时间)
- ChannelBind:绑定信道号(CHANNEL-NUMBER)与特定Peer地址,启用ChannelData传输
- Send/Data Indication:用于中继数据传输(Send为客户端→服务器,Data为服务器→客户端
核心属性类型
新增属性包括:
- XOR-RELAYED-ADDRESS:服务器分配的中继地址(IP+端口),用于标识客户端的中继端点
- XOR-MAPPRE-ADDRES: 本机映射外网地址(IP+port)
- CHANNEL-NUMBER:2字节信道号(范围0x4000-0x7FFF),用于ChannelData报文的高效传输
- LIFETIME:中继地址有效期(默认10分钟),客户端需定期刷新
- DONT-FRAGMENT:标志位,指示客户端支持IP分片重组
错误码与状态
TURN协议扩展了STUN错误码:
- 401(未认证):请求未携带有效凭证,需重新发送认证信息78
- 437(Allocation Mismatch):客户端尝试使用未分配的信道或地址
- 508(Insufficient Capacity):服务器资源不足,无法分配中继地址
数据传输机制
1. Send/Data Indication:
- 使用STUN消息格式,Send携带应用数据发送至服务器,Data由服务器转发至Peer
- 头部包含XOR-PEER-ADDRESS属性,标识目标Peer地址67。
- ChannelData消息:
- 非STUN格式,头部仅4字节(信道号+数据长度),减少传输开销
- 需通过ChannelBind预先绑定信道号与Peer地址
③ 发送Allocate Request UDP 信息包信息

第一次返回 401 Unauthorized 信息 带有NONE(新生成的一次性随机数),REALM(标识服务器或服务的域)

再次发送 Allocate Request UDP 包 带上 协议、用户名和sha-1(user:realm:paasword)签名

TURN服务返回 本机映射外网地址和TURN服务分配中继地址、有效时间

④ 发送CreatePermission Request

返回数据

发送数据Send Indication
!【】

接收 Data Indiaction

⑤ Channel-Bind Request
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgithub.com%2Fchensongpoixs%2Fcturn_relay_demo&pos_id=img-B8Tiv02W-1743192961278)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fchensongpoixs.github.io%2F&pos_id=img-om16XyoI-1743192922198)

发送数据 ChannelData Message

2、 TURN测试Demo
TURN测试demo

二 、 iceCandidate收集的时机有两种
1、创建PeerConnection对象传入的参数ice_candidate_pool_size大于0会创建连接池的个数, 如果没有不会创建连接池ICE的速度就会有点慢
设置ice配置
bool Conductor::CreatePeerConnection(bool dtls) {RTC_DCHECK(peer_connection_factory_);RTC_DCHECK(!peer_connection_);RTC_LOG(LS_INFO) << __FUNCTION__;webrtc::PeerConnectionInterface::RTCConfiguration config;config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; //这个 config.enable_dtls_srtp = dtls; //是否加密webrtc::PeerConnectionInterface::IceServer server;server.uri = GetPeerConnectionString();config.ice_candidate_pool_size = 0; // 大于0 就在sdp之前进行stun和turn连接创建, 反之啥事情都不做server.username = user_name_;server.password = pass_word_;std::vector<std::string> turnservers;turnservers.push_back(turn_url_);server.urls = turnservers;// 设置走 turn server 进行转发媒体数据 哈config.type = webrtc::PeerConnectionInterface::kRelay;config.servers.push_back(server);peer_connection_ = peer_connection_factory_->CreatePeerConnection(config, nullptr, nullptr, this);return peer_connection_ != nullptr;
}
在CreatePeerConnection方法中
- 异步创建BasicPortAllocator对象中如果ice_candidate_pool_size大于在SetConfiguration方法中就会进行连接turn和stun服务
- 创建负责ICE管理类JsepTransportController
bool PortAllocator::SetConfiguration(const ServerAddresses& stun_servers,const std::vector<RelayServerConfig>& turn_servers,int candidate_pool_size,bool prune_turn_ports,webrtc::TurnCustomizer* turn_customizer,const absl::optional<int>& stun_candidate_keepalive_interval)
{CheckRunOnValidThreadIfInitialized();// A positive candidate pool size would lead to the creation of a pooled// allocator session and starting getting ports, which we should only do on// the network thread.RTC_DCHECK(candidate_pool_size == 0 || thread_checker_.IsCurrent());bool ice_servers_changed = (stun_servers != stun_servers_ || turn_servers != turn_servers_);stun_servers_ = stun_servers;turn_servers_ = turn_servers;prune_turn_ports_ = prune_turn_ports;if (candidate_pool_frozen_) {if (candidate_pool_size != candidate_pool_size_) {RTC_LOG(LS_ERROR) << "Trying to change candidate pool size after pool was frozen.";return false;}return true;}if (candidate_pool_size < 0) {RTC_LOG(LS_ERROR) << "Can't set negative pool size.";return false;}candidate_pool_size_ = candidate_pool_size;// If ICE servers changed, throw away any existing pooled sessions and create// new ones.///// 20250328 ice[stun、turn]信息改变 就清除当前端口连接池会话if (ice_servers_changed) {pooled_sessions_.clear();}turn_customizer_ = turn_customizer;// If |candidate_pool_size_| is less than the number of pooled sessions, get// rid of the extras.while (candidate_pool_size_ < static_cast<int>(pooled_sessions_.size())) {pooled_sessions_.back().reset(nullptr);pooled_sessions_.pop_back();}///// |stun_candidate_keepalive_interval_| will be used in STUN port allocation// in future sessions. We also update the ready ports in the pooled sessions.// Ports in sessions that are taken and owned by P2PTransportChannel will be// updated there via IceConfig.// ICE的心跳包 的stun_candidate_keepalive_interval_ = stun_candidate_keepalive_interval;for (const auto& session : pooled_sessions_) {session->SetStunKeepaliveIntervalForReadyPorts(stun_candidate_keepalive_interval_);}// If |candidate_pool_size_| is greater than the number of pooled sessions,// create new sessions.while (static_cast<int>(pooled_sessions_.size()) < candidate_pool_size_) {IceParameters iceCredentials = IceCredentialsIterator::CreateRandomIceCredentials();PortAllocatorSession* pooled_session = CreateSessionInternal("", 0, iceCredentials.ufrag, iceCredentials.pwd);pooled_session->set_pooled(true);//20250327 触发MSG_CONFIG_START信号 探测stun和turn 服务连通性 pooled_session->StartGettingPorts();pooled_sessions_.push_back(std::unique_ptr<PortAllocatorSession>(pooled_session));}return true;
}
2、 设置sdp时获取 iceCandidate
整体调用流程

调用流程图

bool TurnPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,const char* data,size_t size,const rtc::SocketAddress& remote_addr,int64_t packet_time_us) {if (socket != socket_) {// The packet was received on a shared socket after we've allocated a new// socket for this TURN port.return false;}// This is to guard against a STUN response from previous server after// alternative server redirection. TODO(guoweis): add a unit test for this// race condition.if (remote_addr != server_address_.address) {RTC_LOG(LS_WARNING) << ToString()<< ": Discarding TURN message from unknown address: "<< remote_addr.ToString() << " server_address_: "<< server_address_.address.ToString();return false;}// The message must be at least the size of a channel header.if (size < TURN_CHANNEL_HEADER_SIZE) {RTC_LOG(LS_WARNING) << ToString()<< ": Received TURN message that was too short";return false;}if (state_ == STATE_DISCONNECTED) {RTC_LOG(LS_WARNING)<< ToString()<< ": Received TURN message while the TURN port is disconnected";return false;}// Check the message type, to see if is a Channel Data message.// The message will either be channel data, a TURN data indication, or// a response to a previous request.uint16_t msg_type = rtc::GetBE16(data);if (IsTurnChannelData(msg_type)) {HandleChannelData(msg_type, data, size, packet_time_us);return true;}if (msg_type == TURN_DATA_INDICATION) {HandleDataIndication(data, size, packet_time_us);return true;}if (SharedSocket() && (msg_type == STUN_BINDING_RESPONSE ||msg_type == STUN_BINDING_ERROR_RESPONSE)) {RTC_LOG(LS_VERBOSE)<< ToString()<< ": Ignoring STUN binding response message on shared socket.";return false;}// This must be a response for one of our requests.// Check success responses, but not errors, for MESSAGE-INTEGRITY.
#if TURN_LOGstd::cout << "[turn][hash() = " << hash() << "]" << std::endl;RTC_LOG(LS_INFO) << "[turn][hash() = " << hash() << "]";
#endif // #if TURN_LOGif (IsStunSuccessResponseType(msg_type) &&!StunMessage::ValidateMessageIntegrity(data, size, hash())) {RTC_LOG(LS_WARNING) << ToString()<< ": Received TURN message with invalid ""message integrity, msg_type: "<< msg_type;return true;}request_manager_.CheckResponse(data, size);return true;
}
总结
WebRTC源码分析地址:https://github.com/chensongpoixs/cwebrtc
相关文章:
WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试
WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试 WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试 WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试前言一、TURN协议1、连接Turn Server 流程①…...
HTTP---基础知识
天天开心!!! 文章目录 一、HTTP基本概念1. 什么是HTTP,又有什么用?2. 一次HTTP请求的过程3.HTTP的协议头4.POST和GET的区别5. HTTP状态码6.HTTP的优缺点 二、HTTP的版本演进1.各个版本的应用场景2、注意要点 三、HTTP与…...
Redis6数据结构之List类型
redis的List类型底层结构是双向链表,插入删除时间复杂度O(1)快,查找为O(n)慢。 应用场景:简单队列、最新评论列表、非实时排行榜(定时计算榜单,如笔记本日销榜单)。 常用命令: lpush将一个或多个值从左边…...
DeepSeek接入飞书多维表格,效率起飞!
今天教大家把DeepSeek接入飞书表格使用。 准备工作:安装并登录飞书;可以准备一些要处理的数据,确保数据格式正确,如 Excel、CSV 等,也可直接存储到飞书多维表格。 创建飞书多维表格:打开飞书,点…...
[FGPA基础学习]分秒计数器的制作
分秒计数器设计 本次实验内容为:DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能 一、系统架构设计 顶层模块划分 顶层模块(top) ├── 按键消抖模块(key_debounce) ├…...
【Spring Boot 与 Spring Cloud 深度 Mape 之十】体系整合、部署运维与进阶展望
【Spring Boot 与 Spring Cloud 深度 Mape 之十】体系整合、部署运维与进阶展望 #微服务实战 #Docker #Kubernetes #SpringSecurity #OAuth2 #分布式事务 #Seata #ServiceMesh #总结 #SpringCloud #SpringBoot 系列终章:经过前九篇 [【深度 Mape 系列】] 的系统学习…...
【商城实战(97)】ELK日志管理系统的全面应用
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想…...
高并发系统下的订单号生成服务设计与实现
目录 引言 订单号设计的关键考量因素 基础需求分析 唯一性保障 数据量预估 可读性设计 系统架构考量 分库分表兼容 可扩展性设计 技术选型与比较 性能优化 高可用性保障 实践案例:高并发系统订单号结构设计 结构详解 业务类型标识(2位) 唯一标识部分…...
每日算法-250329
记录今天学习的三道算法题:两道滑动窗口和一道栈的应用。 2904. 最短且字典序最小的美丽子字符串 题目描述 思路 滑动窗口 解题过程 题目要求找到包含 k 个 ‘1’ 的子字符串,并且需要满足两个条件: 最短长度:在所有包含 k 个 …...
向量数据库学习笔记(2) —— pgvector 用法 与 最佳实践
关于向量的基础概念,可以参考:向量数据库学习笔记(1) —— 基础概念-CSDN博客 一、 pgvector简介 pgvector 是一款开源的、基于pg的、向量相似性搜索 插件,将您的向量数据与其他数据统一存储在pg中。支持功能包括&…...
蓝桥杯 之 二分
文章目录 习题肖恩的n次根分巧克力2.卡牌 二分是十分重要的一个算法,常常用于求解一定范围内,找到满足条件的边界值的情况主要分为浮点数二分和整数二分二分问题,最主要是写出这个check函数,这个check函数最主要就是使用模拟的方法…...
从零开始研发GPS接收机连载——18、北斗B1的捕获和跟踪
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 从零开始研发GPS接收机连载——18、北斗B1的捕获和跟踪 B1信号的捕获B1信号的跟踪 前面已经验证了射频能够接收到B1的信号,通过FPGA采集了IQ信号之后能够通过matl…...
sqlalchemy:将mysql切换到OpenGauss
说明 之前python的项目使用的mysql,近期要切换到国产数据库OpenGauss。 之前的方案是fastapisqlalchemy,测试下来发现不用改代码,只要改下配置即可。 切换方案 安装openGauss-connector-python-psycopg2 其代码工程在:https:…...
memtest86检测内存
上次在R730安装万兆网卡的时候,使用过memtest64进行测试。这个操作肯定是有用的,我就用这个方法查出有问题的内存条,及时找商家进行了更换。 但是该方案有个问题,只能锁定部分内存,如下图,只测试到了23G左…...
OpenCV图像拼接(10)用于实现图像拼接过程中的时间流逝(timelapse)效果的一个类cv::detail::Timelapser
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::detail::Timelapser 是 OpenCV 库中用于实现图像拼接过程中的时间流逝(timelapse)效果的一个类。它通常用于将一系列…...
验证linux多进程时间片切换的程序
一、软件需求 在同时运行一个或多个一味消耗 CPU 时间执行处理的进程时,采集以下统计信息。 ・在某一时间点运行在逻辑 CPU 上的进程是哪一个 ・每个进程的运行进度 通过分析这些信息,来确认本章开头对调度器的描述是否正确。实验程序的…...
【Basys3】外设-灯和数码管
灯 约束文件 set_property PACKAGE_PIN W5 [get_ports CLK] set_property PACKAGE_PIN U18 [get_ports rst] set_property PACKAGE_PIN U16 [get_ports {led[0]}] set_property PACKAGE_PIN E19 [get_ports {led[1]}] set_property PACKAGE_PIN U19 [get_ports {led[2]}] set…...
axios文件下载使用后端传递的名称
java后端通过HttpServletResponse 返回文件流 在Content-Disposition中插入文件名 一定要设置Access-Control-Expose-Headers,代表跨域该Content-Disposition返回Header可读,如果没有,前端是取不到Content-Disposition的,可以在统…...
从零开始搭建Anaconda环境
Anaconda是一个流行的Python数据科学平台,包含conda包管理器、Python解释器和大量预装的数据科学工具包。以下是详细的安装和配置步骤: 1. 下载Anaconda 访问Anaconda官方网站 根据你的操作系统(Windows/macOS/Linux)选择相应版本 推荐下载Python 3.x版…...
TDengine 中的命名与边界
简介 本章主要介绍命名的合法字符集和限制规则,这对于正确使用 TDengine,减小报错很重要,这些规则在 SQL 语句中都生效,在使用过程中要注意,避免不必要的错误。 名称命名规则 合法字符:英文字符、数字和…...
软件架构设计中的软件过程模型初识
软件架构设计中的软件过程模型是指导软件开发过程的框架,它们定义了软件开发的不同阶段、活动、任务和角色。结合具体的使用场景,可以更好地理解这些模型如何在实际项目中应用。以下将详细介绍几种常见的软件过程模型,并结合典型场景进行讲解…...
基于ssm的课程辅助教学平台(全套)
互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对《离散结构》课程教学信息管理混乱,出错率高,信息安…...
[创业之路-344]:战略的本质是选择、聚焦, 是成本/效率/低毛利优先,还是差易化/效益/高毛利优先?无论是成本优先,还是差易化战略,产品聚焦是前提。
前言: 一、战略的本质是选择、聚焦 关于战略的本质,触及了商业竞争的核心矛盾:选择成本优先(效率/低毛利)还是差异化(效益/高毛利),本质上是对企业战略方向的终极拷问。 1、战略选…...
洛谷题单1-P5705 【深基2.例7】数字反转-python-流程图重构
题目描述 输入一个不小于 100 100 100 且小于 1000 1000 1000,同时包括小数点后一位的一个浮点数,例如 123.4 123.4 123.4 ,要求把这个数字翻转过来,变成 4.321 4.321 4.321 并输出。 输入格式 一行一个浮点数 输出格式 …...
Typora 小乌龟 git 上传到gitee仓库教程
首先进行资源分享 通过网盘分享的文件:TortoiseGit-LanguagePack-2.17.0.0-64bit-zh_CN.msi等4个文件 链接: https://pan.baidu.com/s/1NC8CKLifCEH_YixDU3HG_Q?pwdqacu 提取码: qacu --来自百度网盘超级会员v3的分享 首先将软件进行解压 看自己电脑的版本进行…...
【新人系列】Golang 入门(八):defer 详解 - 上
✍ 个人博客:https://blog.csdn.net/Newin2020?typeblog 📝 专栏地址:https://blog.csdn.net/newin2020/category_12898955.html 📣 专栏定位:为 0 基础刚入门 Golang 的小伙伴提供详细的讲解,也欢迎大佬们…...
知识图谱之知识抽取:从数据海洋中 “捞金”
目录 知识抽取:开启知识宝库的钥匙 知识抽取的对象:实体、关系与属性 知识抽取的方法:各显神通的 “淘金术” 基于规则的方法 机器学习方法 深度学习方法 知识抽取面临的挑战:荆棘丛中的探索 数据的多样性和复杂性 语义理…...
RAG - 五大文档切分策略深度解析
文章目录 切分策略1. 固定大小分割(Fixed-Size Chunking)2. 滑动窗口分割(Sliding Window Chunking)3. 自然语言单元分割(Sentence/Paragraph Segmentation)4. 语义感知分割(Semantic-Aware Seg…...
keil中文注释出现乱码怎么解决
keil中文注释出现乱码怎么解决 在keil–edit–configuration中encoding改为chinese-GB2312...
论文阅读笔记——ReconDreamer
ReconDreamer 论文 在 DriveDreamer4D 的基础上,通过渐进式数据更新,解决大范围机动(多车道连续变道、紧急避障)的问题。同时 DriveDreamer4D生成轨迹后直接渲染,而 ReconDreamer 会实时通过 DriveRestorer 检测渲染结…...
