WebRTC之P2P

StoneLiu999 2020-11-19 11:35:39 802 已收藏 4

分类专栏: WebRTC 文章标签: webrtc p2p turn nat stun
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/momo0853/article/details/109806172

版权

文章目录

  • SDP/STUN/TURN/ICE
    • SDP
    • STUN
      • 服务端实现
      • 客户端实现
      • NAT类型判断
    • TURN
      • TurnServer(TURN服务端)
      • TurnPort(TURN客户端)
    • ICE
  • NAT类型
    • 完全圆锥形NAT(Full cone NAT)
    • 受限圆锥形NAT(Address-Restricted cone NAT)
    • 端口受限圆锥形NAT(Port-Restricted cone NAT)
    • 对称NAT(Symmetric NAT)

SDP/STUN/TURN/ICE

对这几种名称进行简单介绍如下:

  • SDP是一种用于描述媒体信息的标准协议,例如分辨率、编码器、加密等
  • Offer/Answer,我们要和对端交换的描述信息就称为Offer,对端发给我们的描述信息就称为Answer,不同客户端支持的编解码类型是不一样的,所以需要协商
  • STUN是一种获取NAT公网IP,以及NAT类型的协议
  • TURN是在STUN基础上增加转发功能的协议
  • ICE就是把STUN和TURN的结合

SDP

以下SDP内容完全来自维基-Session Description Protocol,阅读原文获取更全面的信息。

SDP是用于描述流媒体通信参数的格式。IETF在1998年4月发布了原始规范作为拟议标,随后在2006年7月发布了修订的规范RFC4566。
SDP用于会话描述通告,会话邀请和参数协商等多媒体通信会话。SDP本身并不传递任何媒体,而是在端点之间用于协商媒体类型,格式和所有相关属性。属性和参数的集合通常称为会话配置文件。
SDP被设计为可扩展的,以支持新的媒体类型和格式。SDP最初是作为会话公告协议(SAP)的组成部分,但发现它与实时传输协议(RTP),实时流协议(RTSP),会话发起协议(SIP)和即使是用于描述多播会话的独立格式。

STUN

以下STUN内容完全来自维基-STUN,阅读原文获取更全面的信息。

STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间创建UDP通信。该协议由RFC 5389定义。

一旦客户端得知了Internet端的UDP端口,通信就可以开始了。如果NAT是完全圆锥型的,那么双方中的任何一方都可以发起通信。如果NAT是受限圆锥型或端口受限圆锥型,双方必须一起开始传输。

STUN使用下列的算法(取自RFC 3489)来发现NAT gateways类型以及防火墙(firewalls)

一旦路经通过红色箱子的终点时,UDP的沟通是没有可能性的。一旦通过黄色或是绿色的箱子,就有连线的可能。

  • 1.STUN客户端向STUN服务器发送请求,要求得到自身经NAT映射后的地址:

    • a. 收不到服务器回复,则认为UDP被防火墙阻断,不能通信,网络类型:Blocked.
    • b. 收到服务器回复,对比本地地址,如果相同,则认为无NAT设备,进入第2步,否则认为有NAT设备,进入3步.
  • 2.(已确认无NAT设备)STUN客户端向STUN服务器发送请求,要求服务器从其他IP和PORT向客户端回复包:

    • a. 收不到服务器从其他IP地址的回复,认为包被前置防火墙阻断,网络类型:Symmetric UDP Firewall.
    • b. 收到则认为客户端处在一个开放的网络上,网络类型:Opened.
  • 3.(已确认存在NAT设备)STUN客户端向STUN服务器发送请求,要求服务器从其他IP和PORT向客户端回复包:

    • a. 收不到服务器从其他IP地址的回复,认为包被前置NAT设备阻断,进入第4步.
    • b. 收到则认为NAT设备类型为Full Cone,即网络类型:Full Cone NAT.
  • 4.STUN客户端向STUN服务器的另外一个IP地址发送请求,要求得到自身经NAT映射后的地址,并对比之:

    • a. 地址不相同,则网络类型:Symmetric NAT.
    • b. 相同则认为是Restricted NAT,进入第5步,进一步确认类型.
  • 5.(已确认Restricted NAT设备)STUN客户端向STUN服务器发送请求,要求服务器从相同IP的其他PORT向客户端回复包:

    • a. 收不到服务器从其他PORT地址的回复,认为包被前置NAT设备阻断,网络类型:Port Restricted cone NAT.
    • b. 收到则认为网络类型: Restricted cone NAT.

有了上面的理论以后,我们来看看WebRTC的代码。WebRTC实现了STUN的功能,包括了客户端和服务端以及NAT探测,它使用的是RFC 5389协议。

服务端实现

WebRTC的STUN实现stunserver.cc很简单,收到stun客户端的请求,然后把客户端的最外层地址返回给用户。

// 判断是不是stun格式,如果是拍判断消息类型,目前仅仅支持STUN_BINDING_REQUEST消息
void StunServer::OnPacket(rtc::AsyncPacketSocket* socket, const char* buf, size_t size,const rtc::SocketAddress& remote_addr,const rtc::PacketTime& packet_time) {// Parse the STUN message; eat any messages that fail to parse.rtc::ByteBufferReader bbuf(buf, size);StunMessage msg;if (!msg.Read(&bbuf)) {return;}// Send the message to the appropriate handler function.switch (msg.type()) {case STUN_BINDING_REQUEST:OnBindingRequest(&msg, remote_addr);break;default:SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported");}
}
// 构建一个stun的response消息,并写入stun客户的外网地址
void StunServer::OnBindingRequest(StunMessage* msg, const rtc::SocketAddress& remote_addr) {StunMessage response;GetStunBindReqponse(msg, remote_addr, &response);SendResponse(response, remote_addr);
}
// 返回response数据给客户端
void StunServer::SendResponse(const StunMessage& msg, const rtc::SocketAddress& addr) {rtc::ByteBufferWriter buf;msg.Write(&buf);rtc::PacketOptions options;if (socket_->SendTo(buf.Data(), buf.Length(), addr, options) < 0)LOG_ERR(LS_ERROR) << "sendto";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

客户端实现

客户端的实现简单来说就是构建一个标准的STUN消息请求并发送给不同的STUN服务器(或许存在多个不同的STUN服务器,我还不能明白存在多个STUN服务器的意义),并收集STUN服务器返回的自身的外网IP,并放到candidates中。虽然代码看着不少,因为代码要考虑健壮性。
CreateStunPorts -> StunPort::Create -> new StunPort -> new UDPPort -> UDPPort::Init -> UDPPort::OnLocalAddressReady -> StunRequestManager::SendDelayed -> UDPPort::OnSendPacket -> UDPPort::OnReadPacket -> StunBindingRequest::OnResponse -> StunRequestManager::CheckResponse -> UDPPort::OnStunBindingRequestSucceeded

// 构建一个StunPort,StunPort是对见Port的简单封装
void AllocationSequence::CreateStunPorts() {...StunPort* port = StunPort::Create(session_->network_thread(), session_->socket_factory(), network_,session_->allocator()->min_port(), session_->allocator()->max_port(),session_->username(), session_->password(), config_->StunServers(),session_->allocator()->origin());...
}
// 初始化StunPort
static StunPort* Create(rtc::Thread* thread, rtc::PacketSocketFactory* factory,  rtc::Network* network, ...) {StunPort* port = new StunPort(thread, factory, network, min_port, max_port, username, password, servers, origin);if (!port->Init()) { delete port; port = NULL;  }
}
// StunPort是对UDPPort的子类
StunPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory,  rtc::Network* network, uint16_t min_port,  uint16_t max_port,  ...): UDPPort(thread, factory, network, min_port, max_port, username, password, origin, false) {// UDPPort will set these to local udp, updating these to STUN.set_type(STUN_PORT_TYPE);set_server_addresses(servers);
}
// 创建一个UDP Socket,这个Socket也是WebRTC的封装,不细说,当Socket的状态发生变化的时候会通过Socket的`Signal`信号槽回调出来
bool UDPPort::Init() {...socket_ = socket_factory()->CreateUdpSocket(rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port());socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket);socket_->SignalSentPacket.connect(this, &UDPPort::OnSentPacket);socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend);socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady);requests_.SignalSendPacket.connect(this, &UDPPort::OnSendPacket);return true;
}
// Socket地址可用回调此函数,在这里会先收集local candiate,然后调用MaybePrepareStunCandidate获取本机的外网地址
void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket, const rtc::SocketAddress& address) {rtc::SocketAddress addr = address;MaybeSetDefaultLocalAddress(&addr);AddAddress(addr, addr, rtc::SocketAddress(), UDP_PROTOCOL_NAME, "", "", LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST, 0, "", false);MaybePrepareStunCandidate();
}
// 判断是否需要向STUN服务器请求本机外网地址或者判断是否完成了获取外网地址的请求
void UDPPort::MaybePrepareStunCandidate() {if (!server_addresses_.empty()) {SendStunBindingRequests();} else {MaybeSetPortCompleteOrError();}
}
// 依次向不同的服务器请求外网地址
void UDPPort::SendStunBindingRequests() {for (ServerAddresses::const_iterator it = server_addresses_.begin(); it != server_addresses_.end(); ++it) {SendStunBindingRequest(*it);}
}
// 如果STUN服务器地址可用,那么向此服务器发送一个binding请求
void UDPPort::SendStunBindingRequest(const rtc::SocketAddress& stun_addr) {if (stun_addr.IsUnresolvedIP()) {ResolveStunAddress(stun_addr);} else if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {if (IsCompatibleAddress(stun_addr)) {requests_.Send(new StunBindingRequest(this, stun_addr, rtc::TimeMillis()));} else {OnStunBindingOrResolveRequestFailed(stun_addr);}}
}
// 进一步完成StunRequest的设定,并把此数据发送给stun服务器
void StunRequestManager::SendDelayed(StunRequest* request, int delay) {request->set_manager(this);RTC_DCHECK(requests_.find(request->id()) == requests_.end());request->set_origin(origin_);request->Construct();requests_[request->id()] = request;if (delay > 0) {thread_->PostDelayed(RTC_FROM_HERE, delay, request, MSG_STUN_SEND, NULL);} else {thread_->Send(RTC_FROM_HERE, request, MSG_STUN_SEND, NULL);}
}
// 通过SignalSendPacket发送数据,紧接着判断已经发送的次数,以及超时情况
void StunRequest::OnMessage(rtc::Message* pmsg) {tstamp_ = rtc::TimeMillis();rtc::ByteBufferWriter buf;msg_->Write(&buf);manager_->SignalSendPacket(buf.Data(), buf.Length(), this);OnSent();manager_->thread_->PostDelayed(RTC_FROM_HERE, resend_delay(), this, MSG_STUN_SEND, NULL);
}
// 调用udp socket把数据发出去
void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req);rtc::PacketOptions options(DefaultDscpValue());if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0)PLOG(LERROR, socket_->GetError()) << "sendto";
}
// 如果是stun服务器返回的消息,则调用CheckResponse检查是啥消息
void UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket, const char* data,  size_t size, const rtc::SocketAddress& remote_addr,  const rtc::PacketTime& packet_time) {if (server_addresses_.find(remote_addr) != server_addresses_.end()) {requests_.CheckResponse(data, size);return;}


http://www.taodudu.cc/news/show-3096328.html

相关文章:

  • p2p传输
  • 树莓派进阶之路 (038) - P2P 文件下载机
  • P2P原理 和技术演变(概念性质)
  • 红米K50电竞版上手体验
  • P2P 应用程序框架
  • p2p 网络基础 网络高并发
  • 边下边看 七款P2P下载软件全能大比拼
  • 迅雷同时下载的人数越多,BT下载越快的奥秘——另辟蹊径的P2P应用
  • 计算机网络 :P2P文件分发
  • 利用P2P软件(Murder)大规模分发大文件
  • 有没有支持P2P架构的直播技术?
  • 计算机网络--应用层(2)P2P应用
  • 音乐下载py
  • openwrt 开启p2p下载后不定时断流处理办法
  • P2P文件分发读书笔记
  • P2P打洞技术详解
  • 百行go代码构建p2p聊天室
  • p2p杂摘
  • android WebRtc 视频通话(P2P)
  • golang打造p2p网络
  • 【Linux项目】 --P2P下载器的详细介绍
  • java p2p 下载_java p2p文件传输(含服务器端与jsp源码)
  • 2019年5月17日A股暴跌行情思考
  • 程序化交易如何影响A股?
  • A股市场情况,
  • 还是到点说说A股
  • 继续聊聊A股
  • 依然到点说A股
  • 继续聊A股
  • 2019年4月22日A股暴跌行情思考

WebRTC之P2P相关推荐

  1. 流媒体协议之WebRTC实现p2p视频通话(二)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 简介 目的 帮助自己了解webrtc 实现端对端通信 # 使用流 ...

  2. WebRTC 实现P2P音视频通话——原生IOS端使用WebRTC实现一对一音视频通话

    IOS端使用WebRTC实现一对一音视频通话 前言 环境 一.环境配置 搭建项目,配置权限,通过CocoaPods安装第三方库 二.音视频通话的实现 音视频通话实现主要分为两部分,信令客户端以及web ...

  3. WebRTC 实现P2P音视频通话——搭建信令服务器

    WebRTC 实现P2P音视频通话--搭建信令服务器 文章目录 WebRTC 实现P2P音视频通话--搭建信令服务器 前言 一.安装NodeJS,npm 二.服务器端实现 1.引入库 2.代码实现 3 ...

  4. 基于COTURN实现WebRTC的P2P项目

    基于COTURN实现WebRTC的P2P项目 WebRTC coturn WebServer 连接时序图 编码 前端 后端 编写前端主要逻辑 依赖 完整代码 WebRTC webrtc是google推 ...

  5. WebRTC 实现P2P音视频通话——实现一对一音视频通话

    WebRTC 实现P2P音视频通话 WebRTC 实现P2P音视频通话--搭建信令服务器 WebRTC 实现P2P音视频通话--搭建stun/trun P2P穿透和转发服务器 WebRTC 实现P2P ...

  6. WebRTC 实现P2P音视频通话——搭建stun/turn P2P穿透和转发服务器

    WebRTC 实现P2P音视频通话 WebRTC 实现P2P音视频通话--搭建信令服务器 WebRTC 实现P2P音视频通话--搭建stun/turn P2P穿透和转发服务器 文章目录 WebRTC ...

  7. 基于 WebRTC 的 P2P 文件传输

    前言 WebRTC 是一个实时通信的技术,它提供了一套 API,可以让浏览器实现 P2P 通信,而且不需要额外的插件,这使得 WebRTC 成为了一种非常有前景的技术.在前面几篇文章中,我们已经介绍了 ...

  8. android WebRtc 视频通话(P2P)

    概述 WebRTC名称源自网页实时通信(Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的技术,是谷歌2010年以6820万美元收购Glo ...

  9. WebRTC:P2P音视频通话基础概述

    前言 本篇文章参考WebRTC基础知识详解_签约计划_IT酷盖_InfoQ写作社区,介绍了P2P音视频通信的场景下的一些基础知识,包括WebRTC的基本架构.协议栈,一对一通话基础,和一对一通话原理三 ...

最新文章

  1. DataBinding初探 数据绑定的用法 ,import 集合类型,绑定的表达式,访问集合类型2...
  2. tensorflow详解-tf.nn.conv2d(),tf.nn.max_pool()
  3. 计算最大回撤_量化扫盲:什么是最大回撤?
  4. MySQL(十一)之触发器
  5. 【java】 从hotspot底层对象结构理解锁膨胀升级过程
  6. gds文件转bmp_OFD文件转换服务
  7. VB移动没有标题的窗体
  8. asp.net中控制反转的理解
  9. 苹果8黑屏无法强制开机_iphonexsmax死机黑屏,iphonexsmax无法开机
  10. Git本地仓库的文件夹不显示红色感叹号、绿色对号等图标
  11. 通过V90PN通讯故障实例来看线路干扰与线路错误的区别
  12. 2022最新:8种常用DNA甲基化测序技术,你知道几个?|易基因
  13. 斯芬克怎么样 谁说我没有担心
  14. ffmpeg连接rtsp流提示Connection refused
  15. quorum-maker中遇到的问题
  16. 三防产品外观设计要点
  17. [UE4]传送门:场景切换
  18. 一个菜鸡的HTML标签备忘录【点进来可能瞎眼】
  19. java 分割字符串(多种方法)
  20. 【转载】语音处理资源

热门文章

  1. Part 2 Linux programming:chapter 15:套接字和标准I/O
  2. JAVA-------计算机基础与准备阶段
  3. Java笔记-常用类String
  4. ## 大一java课程设计_航班查询系统(我是小白)
  5. 电子邮件SMTP协议原始命令码和工作原理
  6. 微信小程序之打卡日历
  7. 耦合性(或称“耦合度”)
  8. vue移动端适配—手动rem适配
  9. 破解wifi的渗透工具
  10. 2023年【甘肃省安全员C证】考试题库及甘肃省安全员C证考试总结