zoukankan      html  css  js  c++  java
  • peerconnection_client分析笔记

     Windows版本的peerconnection_client demo是一个win32程序,入口函数为main.cc里面的wWinMain,程序整体流程就从这个入口函数下手开始分析。

    1.peerconnection_client demo中主要的类的关系

    整个demo中有3个主要的类分别是窗口类MainWnd,它的主要功能是实现了一个窗体程序,然后是PeerConnectionClient类,他的作用是与信令服务器来进行TCP通信,最后是联系MainWnd和PeerConnectionClient的类Conductor,Conductor实现了MainWndCallback和PeerConnectionClientObserver接口,当PeerConnectionClient和MainWnd完成某个事件时,会通过调用相应的接口来通知Conductor。
    然后从入口函数wWinMain开始来分析一下demo的函数调用流程

    2.入口函数wWinMain分析

    在函数的一开始,初始化了Windows socket,以及webRTC消息循环。
      rtc::EnsureWinsockInit();
      rtc::Win32Thread w32_thread;
      rtc::ThreadManager::Instance()->SetCurrentThread(&w32_thread);
    然后去处理启动程序的时候传入的命令行参数,感觉不太重要,这里略过。
    处理完命令行参数之后,调用了MainWnd的Create函数创建了窗体
     //创建窗体
      MainWnd wnd(FLAG_server, FLAG_port, FLAG_autoconnect, FLAG_autocall);
      if (!wnd.Create()) {
        RTC_NOTREACHED();
        return -1;
      }

    紧接着就是初始化SSL以及创建PeerConnectionClient和Conductor

      //创建PeerConnectionClient
      //PeerConnectionClient主要用来处理与信令服务器的tcp通讯
      //它有两个Win32Socket:control_socket_和hanging_get_,
      //在PeerConnectionClient::DoConnect()中创建,并在PeerConnectionClient::InitSocketSignals()中连接好socket的信号。
      PeerConnectionClient client;
      //scoped_refptr 是一个智能指针
      //RefCountedObject实现了一个线程安全的引用计数功能
      //代码的作用是创建了一个Conductor对象并用conductor指向它
      rtc::scoped_refptr<Conductor> conductor(
            new rtc::RefCountedObject<Conductor>(&client, &wnd));

    完成了上面的操作之后就进入了窗体消息循环,等待窗体上的操作

    //窗体消息循环
      // Main loop.
      MSG msg;
      BOOL gm;
      //GetMessage函数只有在接收到WM_QUIT消息时才返回0
      while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
        if (!wnd.PreTranslateMessage(&msg)) {
            //将虚拟键消息转换为字符消息
          ::TranslateMessage(&msg);
          //分派一个消息到窗口进程由窗口进程对消息进行处理
          ::DispatchMessage(&msg);
        }
      }
    
      //上面的消息循环退出后,如果仍然链接着,继续处理消息
      if (conductor->connection_active() || client.is_connected()) {
        while ((conductor->connection_active() || client.is_connected()) &&
               (gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
          if (!wnd.PreTranslateMessage(&msg)) {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
          }
        }
      }

    再以后是关闭SSL

    rtc::CleanupSSL();

    3.窗体消息分析

    窗体的消息是在MainWnd的OnMessage函数中进行处理的。
    当点击connect按钮时
          //connect按钮按下
        case WM_COMMAND:
          if (button_ == reinterpret_cast<HWND>(lp)) {
            if (BN_CLICKED == HIWORD(wp))
              OnDefaultAction();
          } else if (listbox_ == reinterpret_cast<HWND>(lp)) {
            if (LBN_DBLCLK == HIWORD(wp)) {
              OnDefaultAction();
            }
          }
          return true;

    点击connect按钮和连接服务器成功之后点击peer名都会进入OnDefaultAction函数

    void MainWnd::OnDefaultAction() {
      if (!callback_)
        return;
      //点击connect按钮
      if (ui_ == CONNECT_TO_SERVER) {
        std::string server(GetWindowText(edit1_));
        std::string port_str(GetWindowText(edit2_));
        int port = port_str.length() ? atoi(port_str.c_str()) : 0;
        //登陆服务器
        callback_->StartLogin(server, port);
        //点击peer名
      } else if (ui_ == LIST_PEERS) {
        LRESULT sel = ::SendMessage(listbox_, LB_GETCURSEL, 0, 0);
        if (sel != LB_ERR) {
          LRESULT peer_id = ::SendMessage(listbox_, LB_GETITEMDATA, sel, 0);
          if (peer_id != -1 && callback_) {
              //连接到peer
            callback_->ConnectToPeer(peer_id);
          }
        }
      } else {
        MessageBoxA(wnd_, "OK!", "Yeah", MB_OK);
      }
    }

    首先看一下怎么连接服务器的

    void PeerConnectionClient::DoConnect() {
        //创建control_socket和hanging_get_两个AsyncSocket,等待socket事件
        //control_socket_和hanging_get_是两个指向AsyncSocket的智能指针
      control_socket_.reset(CreateClientSocket(server_address_.ipaddr().family()));
      hanging_get_.reset(CreateClientSocket(server_address_.ipaddr().family()));
      //连接socket信号和槽
      InitSocketSignals();
      char buffer[1024];
      sprintfn(buffer, sizeof(buffer),
               "GET /sign_in?%s HTTP/1.0
    
    ", client_name_.c_str());
      onconnect_data_ = buffer;
    
      //control_socket_连接服务器,等待连接成功信号,调用OnConnect槽函数
      bool ret = ConnectControlSocket();
      if (ret)
        state_ = SIGNING_IN;
      if (!ret) {
        callback_->OnServerConnectionFailure();
      }
    }

    这里因为是异步的socket,通过注册socket信号的槽函数,会在socket连接成功和读socket的时候触发相应的事件,从而调用和信号绑定的槽函数

    void PeerConnectionClient::InitSocketSignals() {
      RTC_DCHECK(control_socket_.get() != NULL);
      RTC_DCHECK(hanging_get_.get() != NULL);
      // control_socket_关闭信号连接OnClose槽函数
      control_socket_->SignalCloseEvent.connect(this,
          &PeerConnectionClient::OnClose);
      //hanging_get_关闭信号连接OnClose槽函数
      hanging_get_->SignalCloseEvent.connect(this,
          &PeerConnectionClient::OnClose);
      //control_socket_连接信号连接OnConnect槽函数
      control_socket_->SignalConnectEvent.connect(this,
          &PeerConnectionClient::OnConnect);
      //hanging_get_连接信号连接OnHangingGetConnect槽函数
      hanging_get_->SignalConnectEvent.connect(this,
          &PeerConnectionClient::OnHangingGetConnect);
      //control_socket_读信号连接了OnRead槽函数
      control_socket_->SignalReadEvent.connect(this,
          &PeerConnectionClient::OnRead);
      //hanging_get_读信号连接了OnHangingGetRead槽函数
      hanging_get_->SignalReadEvent.connect(this,
          &PeerConnectionClient::OnHangingGetRead);
    }

    所以直接去看PeerConnectionClient的OnConnect函数

    void PeerConnectionClient::OnConnect(rtc::AsyncSocket* socket) {
      RTC_DCHECK(!onconnect_data_.empty());
      //control_socket_连接服务器成功就发送  "GET /sign_in?%s HTTP/1.0
    
    "
      //成功后,服务器会返回当前 channel连接的其他peer ,"200 Added"
      size_t sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length());
      RTC_DCHECK(sent == onconnect_data_.length());
      onconnect_data_.clear();
    }

    连接服务器成功之后会向服务器发送登录请求,成功之后,服务器返回当前channel连接的其他peer名,接着就去看一下PeerConnectionClient的OnRead函数

    void PeerConnectionClient::OnRead(rtc::AsyncSocket* socket) 
    {
         ....
        //peer连接成功
        callback_->OnPeerConnected(id, name);
         ....
            //登录服务器成功之后,切换到显示已登录用户列表UI
        callback_->OnSignedIn();
    }

    这时就到了显示peer名的界面了,当点击peer名时会通过消息循环调用上面的OnDefaultAction函数

    void Conductor::ConnectToPeer(int peer_id) {
      RTC_DCHECK(peer_id_ == -1);
      RTC_DCHECK(peer_id != -1);
    
      if (peer_connection_.get()) {
        main_wnd_->MessageBox("Error",
            "We only support connecting to one peer at a time", true);
        return;
      }
      //初始化PeerConnection
      if (InitializePeerConnection()) {
        peer_id_ = peer_id;
        //创建一个offer!!!!
        peer_connection_->CreateOffer(this, NULL);
      } else {
        main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
      }
    }
    bool Conductor::InitializePeerConnection() {
      RTC_DCHECK(peer_connection_factory_.get() == NULL);
      RTC_DCHECK(peer_connection_.get() == NULL);
    
      //创建PeerConnectionFactory
      peer_connection_factory_  = webrtc::CreatePeerConnectionFactory();
      ....
       //添加stream,切换到stream UI
      AddStreams();
      ....
      }

    然后就开始进行通信了

    void Conductor::AddStreams() {
      if (active_streams_.find(kStreamLabel) != active_streams_.end())
        return;  // Already added.
    
      rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
          peer_connection_factory_->CreateAudioTrack(
              kAudioLabel, peer_connection_factory_->CreateAudioSource(NULL)));
    
      rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
          peer_connection_factory_->CreateVideoTrack(
              kVideoLabel,
              peer_connection_factory_->CreateVideoSource(OpenVideoCaptureDevice(),
                                                          NULL)));
      main_wnd_->StartLocalRenderer(video_track);
      //创建MediaStream采集并传送本地音视频
      rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
          peer_connection_factory_->CreateLocalMediaStream(kStreamLabel);
    
      stream->AddTrack(audio_track);
      stream->AddTrack(video_track);
      if (!peer_connection_->AddStream(stream)) {
        LOG(LS_ERROR) << "Adding stream to PeerConnection failed";
      }
      typedef std::pair<std::string,
                        rtc::scoped_refptr<webrtc::MediaStreamInterface> >
          MediaStreamPair;
      active_streams_.insert(MediaStreamPair(stream->label(), stream));
      //切换到StreamingUI
      main_wnd_->SwitchToStreamingUI();
    }

    5.程序主要流程图

    demo的主要程序流程图如下图所示

    6.后续计划

    因为对整个p2p连接的建立和音视频流的传输等过程还不是很熟悉,所以这篇文章只是浅显的描述了一下demo的函数调用流程,后面会把整个过程的细节理一下,然后自己实现一个peerconnection_client。
     
    参考文章:
    http://blog.csdn.net/qq_24283329/article/category/6915582
  • 相关阅读:
    postman 调试接口报“401 身份认证信息未提供”错误
    UserWarning: XXX is writable by group/others and vulnerable to attack when used with get_resource_filename.
    以root权限执行python时候脚本时候报错“ExtractionError: Can't extract file(s) to egg cache”
    django接口调试示例说明
    查看linux系统版本、内存、CPU、存储容量
    一次批量杀死多个进程
    bash:pybot未找到命令
    Swoft-Api项目部署九:前、后置中间件
    Swoft-Api项目部署八:主从数据库配置
    Swoft-Api项目部署七:验证器
  • 原文地址:https://www.cnblogs.com/CoderTian/p/7868328.html
Copyright © 2011-2022 走看看