delete s; // sorry, that’s all we can take.
printf(“Connection limit reached\n”);
} else {
//如果新连接被成功添加到SocketArray中,则中断会打印改信息
sockets.push_back(s);
printf(“New connection…\n”);
}
}
//在后续的循环中,由于SocketArray中有新的连接,所以会进入如下逻辑
for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) {
DataSocket* s = *i;
if (FD_ISSET(s->socket(), &socket_set)) {
if (s->OnDataAvailable(&socket_done) && s->request_received()) {
//从服务端注册列表中,检索这个client,由于是for循环从begin到end的遍历,
//所以遍历完了之后,所有客户端界面都会显示连上服务器的其它客户端,这样想和谁通信,
//客户端上点击对应的客户端项就行了,如果未找到,返回NULL
ChannelMember* member = clients.Lookup(s);
if (member || PeerChannel::IsPeerConnection(s)) {
//如果发现s是注册,并且这个s并没有在服务端的注册列表中,则会用AddMember将其添加到注册列表中
if (!member) {
if (s->PathEquals(“/sign_in”)) {
clients.AddMember(s);//这里的AddMember会打印记录的客户端名,如截图的gsc@240
}
//如果客户端是等待状态,这是什么也不用做,等到有message时才进行处理
} else if (member->is_wait_request(s)) {
// no need to do anything.
socket_done = false;
//流程到这里,说明这个客户端已经在服务器注册了,并且这次收到的HTTP消息是message,不是wait,sign_out之类的
} else {
//这里找到想要通信客户端(设为B)的ChannelMember对象,如果server端没找到,则返回NULL
ChannelMember* target = clients.IsTargetedRequest(s);
if (target) {
//这里将包含客户端A SDP协议信息转送给B
member->ForwardRequestToPeer(s, target);
} else if (s->PathEquals(“/sign_out”)) {
s->Send(“200 OK”, true, “text/plain”, “”, “”);
}
}
}
}
}
当client点击connect之后,客户端给服务器发过去"/sign_in"的get请求,服务器给当前客户列表中的每个都发一遍新用户的信息,格式是"<client_name>,<client_id>,1\r\n",服务器将现有的peers列表信息以如下格式连接"<client_name>,<client_id>,if_connected?1:0\r\n"发送给新来的client.得到这些信息后客户端就会把他们显示在pees列表中。客户端每次给服务器发送消息之后都要发送一次"/wait"的get请求,目的是提供response对象,以便服务器在需要给客户端发消息时使用。当客户端双击某个peer发起连接时,会给服务器发来"/message"的post请求,请求的peer_id参数是发起方的id,to参数是要连接的peer的id,内容是sdp信息;服务器把内容转发给相应id的客户端,客户端接收到后解析sdp,得到媒体流信息(是否含音视频,音视频参数等),在本地建立媒体流通道,以便之后接收媒体数据和传给主窗体显示;完了创建本地的sdp,并发回给对方。客户端从STUN/TURN服务器获取本地地址(candidate),经由服务器中转发给对方(同样走/messgae),对方接收到后会向该地址发送消息等待回应以验证是否可连通,联通成功peerconnection就此建立完成,音视频数据就可以通过基于udp的rtp/rtcp协议在peerconnection之间传输。
客户端逻辑
客户端稍微复杂些,和server端相比,多了多媒体内容,但是WebRTC的例子非常巧妙,使用较少的代码就实现待UI的视频通信的完整例子。
Conductor是封装了windows和PeerConnection两个主要类,是该工程的核心,conductor通过CreatePeerConnectionFactory方法创建PeerConnectionFactoryInterface接口的实现对象,通过改接口创建WebRTC核心协议的PeerConnectionInterface接口对象,PeerConnectionFactoryInterface还提供了创建本地音视频功能的接口,conductor的回调通过PeerConnectionObserver接口完成。
Linux UI
linux UI层使用了GTK显示框架,将点击、输入等事件使用信号槽的机制,将界面和需要执行的动作关联起来,如点击加入会议涉及到PeerConnection创建和连接,这里clicked是Button触发的信号,而Button触发时会自动触发row-activated信号,所以下面的两个回调都会被调用到,OnClickedCallback获取用户输入的登录服务IP地址和端口号,并向服务器发起HTTP登录请求,而row-activated信号的回调PeerConnection初始化并创建和发送SDP包。代码实现上将PeerConnection和windows作为两个独立的组件,用conductor类管理起来。
g_signal_connect(button, “clicked”, G_CALLBACK(OnClickedCallback), this);
g_signal_connect(peer_list_, “row-activated”,
G_CALLBACK(OnRowActivatedCallback), this);
P2P的创建过程有些琐碎,这里罗列如下:
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
nullptr /* network_thread */, nullptr /* worker_thread */,
nullptr /* signaling_thread */, nullptr /* default_adm */,
webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory(),
webrtc::CreateBuiltinVideoEncoderFactory(),
webrtc::CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
nullptr /* audio_processing */);