Webrtc从理论到实践一:初识
Webrtc从理论到实践二: 架构
Webrtc从理论到实践三: 角色
Webrtc从理论到实践四: 通信
Webrtc从理论到实践五: 编译webrtc源码
Webrtc从理论到实践六: Webrtc官方demo运行
本文源码基于webrtc m89版本,先从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()接口实现,以下代码片段已做删减
监听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);
使用了一个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请求类型进行相应处理
...
}
...
...
}
当没有客户端连接的时候,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");
}
}
遍历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;
}
}
下一篇:Webrtc从理论到实践八: 官方demo源码走读(peerconnection_client)(上)