WebRTC之P2P
WebRTC之P2P
文章目录
- 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相关推荐
- 流媒体协议之WebRTC实现p2p视频通话(二)
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 简介 目的 帮助自己了解webrtc 实现端对端通信 # 使用流 ...
- WebRTC 实现P2P音视频通话——原生IOS端使用WebRTC实现一对一音视频通话
IOS端使用WebRTC实现一对一音视频通话 前言 环境 一.环境配置 搭建项目,配置权限,通过CocoaPods安装第三方库 二.音视频通话的实现 音视频通话实现主要分为两部分,信令客户端以及web ...
- WebRTC 实现P2P音视频通话——搭建信令服务器
WebRTC 实现P2P音视频通话--搭建信令服务器 文章目录 WebRTC 实现P2P音视频通话--搭建信令服务器 前言 一.安装NodeJS,npm 二.服务器端实现 1.引入库 2.代码实现 3 ...
- 基于COTURN实现WebRTC的P2P项目
基于COTURN实现WebRTC的P2P项目 WebRTC coturn WebServer 连接时序图 编码 前端 后端 编写前端主要逻辑 依赖 完整代码 WebRTC webrtc是google推 ...
- WebRTC 实现P2P音视频通话——实现一对一音视频通话
WebRTC 实现P2P音视频通话 WebRTC 实现P2P音视频通话--搭建信令服务器 WebRTC 实现P2P音视频通话--搭建stun/trun P2P穿透和转发服务器 WebRTC 实现P2P ...
- WebRTC 实现P2P音视频通话——搭建stun/turn P2P穿透和转发服务器
WebRTC 实现P2P音视频通话 WebRTC 实现P2P音视频通话--搭建信令服务器 WebRTC 实现P2P音视频通话--搭建stun/turn P2P穿透和转发服务器 文章目录 WebRTC ...
- 基于 WebRTC 的 P2P 文件传输
前言 WebRTC 是一个实时通信的技术,它提供了一套 API,可以让浏览器实现 P2P 通信,而且不需要额外的插件,这使得 WebRTC 成为了一种非常有前景的技术.在前面几篇文章中,我们已经介绍了 ...
- android WebRtc 视频通话(P2P)
概述 WebRTC名称源自网页实时通信(Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的技术,是谷歌2010年以6820万美元收购Glo ...
- WebRTC:P2P音视频通话基础概述
前言 本篇文章参考WebRTC基础知识详解_签约计划_IT酷盖_InfoQ写作社区,介绍了P2P音视频通信的场景下的一些基础知识,包括WebRTC的基本架构.协议栈,一对一通话基础,和一对一通话原理三 ...
最新文章
- DataBinding初探 数据绑定的用法 ,import 集合类型,绑定的表达式,访问集合类型2...
- tensorflow详解-tf.nn.conv2d(),tf.nn.max_pool()
- 计算最大回撤_量化扫盲:什么是最大回撤?
- MySQL(十一)之触发器
- 【java】 从hotspot底层对象结构理解锁膨胀升级过程
- gds文件转bmp_OFD文件转换服务
- VB移动没有标题的窗体
- asp.net中控制反转的理解
- 苹果8黑屏无法强制开机_iphonexsmax死机黑屏,iphonexsmax无法开机
- Git本地仓库的文件夹不显示红色感叹号、绿色对号等图标
- 通过V90PN通讯故障实例来看线路干扰与线路错误的区别
- 2022最新:8种常用DNA甲基化测序技术,你知道几个?|易基因
- 斯芬克怎么样 谁说我没有担心
- ffmpeg连接rtsp流提示Connection refused
- quorum-maker中遇到的问题
- 三防产品外观设计要点
- [UE4]传送门:场景切换
- 一个菜鸡的HTML标签备忘录【点进来可能瞎眼】
- java 分割字符串(多种方法)
- 【转载】语音处理资源