前面分析webrtc的peerconnection_client协议流程,本文将分析webrtc的peerconnection_server流程。因为整个webrtc 的server分为turn-server,stun-server ,signal-server,这里的peerconnection_server中的server仅仅指的是signal-server。
Webrtc 部署框架
一、信令服务器基本原理
信令服务器的主要是负责呼叫,成员管理等一些控制和管理功能,类似电话的拨号流程。官方的demo的信令服务器比较简单,采用http协议承载交互信令,webrtc对信令协议本身没有定义,用户可以自由选型,你可以用http,websocket,sip,rtsp,甚至用tcp传自定义都无所谓,只要达到两个视频通话的peer能交互信息即可。Webrtc的官方demo,为了演示整个流程,采用传统的http协议,但实际商用,考虑安全,高可靠,性能等因素可能不会简单采用http协议,但协议交互的流程直接我们借鉴。
二、代码详解
peerconnection_server 本质就是一个简单的tcp服务器,这个服务器负责2个peer信息交换。
1)从main函数出发,解析命令行,创建socket,listen 监控端口。
absl::ParseCommandLine(argc, argv);
ListeningSocket listener;
if (!listener.Create()) { //创建监听socket
printf("Failed to create server socket\n");
return -1;
} else if (!listener.Listen(port)) {//监听socket
printf("Failed to listen on server socket\n");
return -1;
}
PeerChannel clients;
2)进入大循环,采用select 网络模型,监听listen端口和accept进程的连接端口
while (!quit) {
fd_set socket_set;
FD_ZERO(&socket_set);
//将监听socket加入监听集
if (listener.valid())
FD_SET(listener.socket(), &socket_set);
//将接收socket加入监听集
for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i)
FD_SET((*i)->socket(), &socket_set);
struct timeval timeout = {10, 0};
//select事件模型监听
if (select(FD_SETSIZE, &socket_set, NULL, NULL, &timeout) == SOCKET_ERROR) {
printf("select failed\n");
break;
}
//遍历socket监听socket集,对已有事件的是socket数据进行接收和处理
for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) {
DataSocket* s = *i;
bool socket_done = true;
if (FD_ISSET(s->socket(), &socket_set)) { //找到有事件的socket
//读取socket数据,并进行解析
if (s->OnDataAvailable(&socket_done) && s->request_received()) {
ChannelMember* member = clients.Lookup(s);
//如何请求socket对应成员存在或者第一次请求连接
if (member || PeerChannel::IsPeerConnection(s)) {
if (!member) {
if (s->PathEquals("/sign_in")) {
clients.AddMember(s);//如何请求成员ID不存在,则将该成员ID加入房间
} else {
printf("No member found for: %s\n", s->request_path().c_str());
s->Send("500 Error", true, "text/plain", "",
"Peer most likely gone.");
//成员找不到,回复500错误
}
} 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")) {
//退出连接,发peer登录响应
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;
}
}
clients.CheckForTimeout();
//新来一个请求连接,将该socket加入监听集
if (FD_ISSET(listener.socket(), &socket_set)) {
DataSocket* s = listener.Accept();
//select监听是个数超过最大值,忽略
if (sockets.size() >= kMaxConnections) {
delete s; // sorry, that's all we can take.
printf("Connection limit reached\n");
} else {
//否则加入socket队列
sockets.push_back(s);
printf("New connection...\n");
}
}
}
这里面的main.cc是最顶层的业务逻辑,会调用data_socket.cpp和peer_channel.cpp,该data_socket负责http数据接收,数据解析,数据封装和响应回复。peer_channel负责房间ID的加入,查询等管理功能。至此整个peerconnection的demo分析完毕,后面我们将封装一个webrtc SDK提供给业务使用或根据需求定制webrtc的某些功能。