系列文章目录

Webrtc从理论到实践一:初识
Webrtc从理论到实践二: 架构
Webrtc从理论到实践三: 角色
Webrtc从理论到实践四: 通信
Webrtc从理论到实践五: 编译webrtc源码
Webrtc从理论到实践六: Webrtc官方demo运行

文章目录

  • 系列文章目录
  • 前言
  • 一、peerconnection_server 目录结构
  • 二、类图
  • 三、工作流程
    • 1. 监听socket端口
    • 2. 开启事件循环
    • 3.根据socket请求类型进行相应处理
      • 3.1 创建新的连接
      • 3.1 接收socket发送的信令
    • 4.流程简图

前言

本文源码基于webrtc m89版本,先从peerconnection_server开始分析


一、peerconnection_server 目录结构


     从图上我们可以看出peerconnection_server的源文件仅有七个:其中main文件主要用于主流程的控制以及网络事件的分发,data_socket文件用于socket的创建与数据的读取和发
送,peer_channel文件用于信令的处理以及socket的管理,utils文件里存放了两个常用的字符串处理函数。

二、类图


    简单介绍一下上面几个类的作用:首先,SocketBase类是对win32 socket api的封装,包含了创建和关闭两个接口。然后,ListeningSocket和DataSocket类都继承了SocketBase类,从他们的命名我们就可以得知,DataSocket的主要职责是用于从socket接收/发送数据,对应的接口是OnDataAvailable()和send()方法。ListeningSocket是用于接收连接和创建DataSocket实例的,Listen()接口用于监听端口,当接收到一个新的连接时就会通过Accept()创建一个新的DataSocket实例。ChannelMember用于处理从DataSocket 接收到的信令,并且与DataSocket是一 一对应的,每个ChannelMember都会保存一个从0开始递增的id_用于标识。PeerChannel类是ChannelMember的管理类,内部创建了一个_members数组用于保存建立连接的ChannelMember,并且可以通过Lookup()接口查找ChannelMember对象,还可以通过AddMember()将ChannelMember对象保存起来。

三、工作流程

首先说一下整个server的模型是事件驱动模型,采用select()接口实现,以下代码片段已做删减

1. 监听socket端口

    监听socket端口主要分为两步:第一个解析命令行参数,从命令行参数中获取指定端口。第二个创建socket 并且监听端口。

// InitFieldTrialsFromString stores the char*, so the char array  //must outlive the application.
const std::string force_field_trials = absl::GetFlag(FLAGS_force_fieldtrials);
webrtc::field_trial::InitFieldTrialsFromString(force_field_trials.c_str());int port = absl::GetFlag(FLAGS_port);// Abort if the user specifies a port that is outside the allowed
// range [1, 65535].
if ((port < 1) || (port > 65535)) {printf("Error: %i is not a valid port.\n", port);return -1;
}ListeningSocket listener;
if (!listener.Create()) {printf("Failed to create server socket\n");return -1;
} else if (!listener.Listen(port)) {printf("Failed to listen on server socket\n");return -1;
}
printf("Server listening on port %i\n", port);

2. 开启事件循环

使用了一个while循环,将listening socket 和所有建立连接创建的socket添加到socket_set中进行select()处理

PeerChannel clients;
typedef std::vector<DataSocket*> SocketArray;
SocketArray sockets;
bool quit = false;
while(!quit){fd_set socket_set;FD_ZERO(&socket_set);//将监听socket添加到socket集合中if (listener.valid())FD_SET(listener.socket(), &socket_set);for (SocketArray::iterator i = sockets.begin(); i   != sockets.end(); ++i)//后面会将新建立连接的socket放到sokets数组中,需要将sockets中所有的socket都放到socket_set集合中进行监听FD_SET((*i)->socket(), &socket_set);struct timeval timeout = {10, 0};if (select(FD_SETSIZE, &socket_set, NULL, NULL, &timeout) == SOCKET_ERROR) {printf("select failed\n");break;}for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) {//根据socket请求类型进行相应处理...}......
}

3.根据socket请求类型进行相应处理

3.1 创建新的连接

    当没有客户端连接的时候,sockets为空,不会进到for循环内部。当listener监听到有新客户端连接时会创建一个新的DataSocket并添加到sockets数组中,接着进入下一轮循环

//将新连接的DataSocket加入到sockets数组中if (FD_ISSET(listener.socket(), &socket_set)) {DataSocket* s = listener.Accept();if (sockets.size() >= kMaxConnections) {delete s;  // sorry, that's all we can take.printf("Connection limit reached\n");} else {sockets.push_back(s);printf("New connection...\n");}}

3.1 接收socket发送的信令

    遍历sockets数组,如果某个socket可读,则通过OnDataAvailable()和request_received()判断当前信令是否可读。在循环之前已经创建了一个PeerChannel clients对象用于管理所有连接的客户端,如果当前数据可读,则通过clients.Lookup(s)查找是否已经包含当前DataSocket,如果不存在,则该用户是第一次发送信令,并且如果http请求的url是“/sign_in”,则根据该DataSocket创建一个ChannelMember对象并加到clients的members_数组中进行统一管理。如果不是新用户,则根据请求的url中的”to=“去查找目标客户端target并转发数据到对端。

for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) {DataSocket* s = *i;bool socket_done = true;if (FD_ISSET(s->socket(), &socket_set)) {if (s->OnDataAvailable(&socket_done) && s->request_received()) {ChannelMember* member = clients.Lookup(s);if (member || PeerChannel::IsPeerConnection(s)) {if (!member) {if (s->PathEquals("/sign_in")) {clients.AddMember(s);} else {printf("No member found for: %s\n", s->request_path().c_str());s->Send("500 Error", true, "text/plain", "","Peer most likely gone.");}} else if (member->is_wait_request(s)) {// no need to do anything.socket_done = false;} else {ChannelMember* target = clients.IsTargetedRequest(s);if (target) {member->ForwardRequestToPeer(s, target);} else if (s->PathEquals("/sign_out")) {s->Send("200 OK", true, "text/plain", "", "");} else {printf("Couldn't find target for request: %s\n",s->request_path().c_str());s->Send("500 Error", true, "text/plain", "","Peer most likely gone.");}}} else {HandleBrowserRequest(s, &quit);if (quit) {printf("Quitting...\n");FD_CLR(listener.socket(), &socket_set);listener.Close();clients.CloseAll();}}}} else {socket_done = false;}if (socket_done) {printf("Disconnecting socket\n");clients.OnClosing(s);assert(s->valid());  // Close must not have been called yet.FD_CLR(s->socket(), &socket_set);delete (*i);i = sockets.erase(i);if (i == sockets.end())break;}}

4.流程简图

以上流程可以简化为下面的流程图:

下一篇:Webrtc从理论到实践八: 官方demo源码走读(peerconnection_client)(上)

Webrtc从理论到实践七: 官方demo源码走读(peerconnection_server)相关推荐

  1. vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析

    最近在做一个视频监控项目的android客户端,要求用rtsp协议完成视频流的传输,但苦于找到不合适的库.之前考虑过用live555或ffmpeg,但涉及到jni调用,加之不熟悉函数调用顺序,开发难度 ...

  2. WebRTC应用问题记录(附项目demo源码)

    为什么icecandidate事件不触发? 必须在setLocalDescription和setRemoteDescription之后,该事件才会触发 为什么手机可以和手机通话,电脑可以和电脑通话,但 ...

  3. winserver的consul部署实践与.net core客户端使用(附demo源码)

    前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是"拆",把臃肿的单块应用,拆分成多个轻量级的服务,每个服务可以在短周期内重构.迭代.交付.随着微服务的数量增多,因量变 ...

  4. vscode插件开发实践与demo源码

    vscode插件开发实践与demo源码 写在前面 工欲善其事必先利其器.vscode作为优秀的开发工具,给我的日常开发工作提供了极大的便利.其拓展机制更是如此. 但是,最近在做年度专业线任务时,有需要 ...

  5. python 自动化微信小程序_干货 | 微信小程序自动化测试最佳实践(附 Python 源码)...

    原标题:干货 | 微信小程序自动化测试最佳实践(附 Python 源码) 本文为霍格沃兹测试学院测试大咖公开课<微信小程序自动化测试>图文整理精华版. 随着微信小程序的功能和生态日益完善, ...

  6. jQuery百叶窗效果原理(附3个demo源码)

    jQuery百叶窗效果原理,附3个demo源码 一.前言 二.原理图解 三.在线演示及源码 一.前言 对于轮播图切换,其实可以有更多效果,其中一类型就是百叶窗.本人参考了网上一些特效,做一些练习和实践 ...

  7. ASP.NET 3.5 新特性开发向导实践(附项目源码下载)

    ASP.NET 3.5 新特性开发向导实践(附项目源码下载) 本篇文章将演示ASP.NET 3.5 部分新功能.新特性,包括LINQ.ListView控件.LinqDataSource.DataPag ...

  8. Api demo源码学习(8)--App/Activity/QuickContactsDemo --获取系统联系人信息

    本节通过Content Provider机制获取系统中的联系人信息,注意这个Anctivity直接继承的是ListActivity,所以不再需要setContentView函数来加载布局文件了(我自己 ...

  9. Api demo源码学习(4)--App/Activity/Dialog --Activity以Dialog形式呈现

    这一节实际上比 Api demo源码学习(2)--App/Activity/Custom Dialog 自定义Activity样式  还要简单一些,在源码学习(2)里,也是让Activity以Dial ...

最新文章

  1. c语言写的跳转心理测试,求各位大神赐教!我做了一个“心理测试的答题卷”编程,总共有1...
  2. oracle+view性能,Oracle 10g的隐含参数_complex_view_merging引发的性能问题
  3. [原创] 腾讯RTX二次开发相关的一些注意事项
  4. (转载)Linux下pthread_once()函数
  5. 虚拟机创建静默快照报错:msg.snapshot.error-QUIESCINGERROR
  6. php怎么自定义设置打印区域,JavaScript_jQuery实现区域打印功能代码详解,使用CSS控制打印样式,需要设 - phpStudy...
  7. WPF,强制捕获鼠标事件,鼠标移出控件外依然可以执行强制捕获的鼠标事件
  8. Oracle分析函数六——数据分布函数及报表函数
  9. LeetCode 1678. 设计 Goal 解析器
  10. oracle主从表分离怎么实时更新数据_高可用数据库UDB主从复制延时的解决
  11. 用Markup Validation Service进行网页的W3C标准语法验证(c#)
  12. Python Tricks(五)—— 计算 list of lists 的长度(元素个数)
  13. c语言用三目运算符从小到大排列,错题集
  14. java list 转json数组_list转json数组
  15. access tempvars 宏_Access数据库实用教程(第2版)第7章宏.ppt
  16. 高中计算机教室标语,高中教室布置标语
  17. 如何将ppt压缩到最小?
  18. Windows下Go语言 幽灵蛛的配置
  19. grafana-普罗米修斯-监控linux-windows版
  20. Xftp的下载、安装、使用

热门文章

  1. (三)k8s资源清单
  2. mysql中什么叫外键_数据库中外键是什么?
  3. 数据结构与算法——十大排序
  4. 性能测试怎么做?性能测试重点和各项性能测试流程(超级详细)
  5. 施工企业信息化应用之路 及“工程承包项目管理软件”(第二届全国工程建设行业信息化高峰论坛讲稿)...
  6. C/C++三种函数入参方法
  7. vue3 + element plus实现侧边栏
  8. 随机指标KDJ使用入门
  9. 拒绝浮躁(copy来的)
  10. iOS-获取UIWebView或者WKWebView页面的视频连接