WebRTC PeerConnection Client源码分析2-PeerConnectionClient

本文分析的webrtc的版本是:m84 平台:win10

WebRTC PeerConnection Client源码分析1-main window

WebRTC PeerConnection Client源码分析2-PeerConnectionClient

WebRTC PeerConnection Client源码分析3-Conductor

在这里插入图片描述

本文用到的抓包数据,可以从这里下载:https://gitee.com/qiuguolu1108/blog

PeerConnectionClient用于和信令服务器进行交互,在Conductor和信令服务器之间起着桥梁的作用。其信令交互时序图如下:
在这里插入图片描述

登录信令服务器

void Conductor::StartLogin(const std::string& server, int port) 
{
  /*如果已经连接了信令服务器,则直接返回。*/
  if (client_->is_connected())
    return;

  server_ = server;
  client_->Connect(server, port, GetPeerName());      /*连接信令服务器*/
}

在connect界面点击Connect按钮后,会触发Conductor::StartLogin()函数,在此函数中会调用PeerConnectionClient::Connect()函数登录信令服务器。

void PeerConnectionClient::Connect(const std::string& server, int port,const std::string& client_name) 
{
  RTC_DCHECK(!server.empty());
  RTC_DCHECK(!client_name.empty());

  /*判断client是否处于连接状态*/
  if (state_ != NOT_CONNECTED) {
    RTC_LOG(WARNING) << "The client must not be connected before you can call Connect()";

    /*调用Conductor::OnServerConnectionFailure,通知Conductor连接失败。*/
    callback_->OnServerConnectionFailure();
    return;
  }

  /*判断ip和client_name是否有效*/
  if (server.empty() || client_name.empty()) {
    callback_->OnServerConnectionFailure();
    return;
  }
  
  /*端口号小于0时,使用默认的值。*/
  if (port <= 0)
    port = kDefaultServerPort;       /*8888*/

  /*保存信令服务器的ip和port*/
  server_address_.SetIP(server);
  server_address_.SetPort(port);

  /*保存client name*/
  client_name_ = client_name;

  /*判断服务器地址是需要解析*/
  if (server_address_.IsUnresolvedIP()){
    state_ = RESOLVING;                     /*设置client的状态为域名解析状态*/
    resolver_ = new rtc::AsyncResolver();   /*创建一个异步的域名解析器*/

    /*注册信号,域名解析结束后会回调这个处理函数。*/
    resolver_->SignalDone.connect(this, &PeerConnectionClient::OnResolveResult);

    /*开始解析域名*/
    resolver_->Start(server_address_);
  } else {     /*如果域名不需要解析,则直接连接信令服务器。*/ 
    DoConnect();
  }
}

用户提供的信令服务器地址需要解析的话,会创建一个异步的域名解析器,因为解析域名是异步的,所以需要注册一个回调函数,用于接收解析后的结果。如果不需要解析地址,则调用DoConnect()直接连接信令服务器。

void PeerConnectionClient::OnResolveResult(rtc::AsyncResolverInterface* resolver) 
{
  if (resolver_->GetError() != 0) {            /*没有解析成功*/
    callback_->OnServerConnectionFailure();
    resolver_->Destroy(false);
    resolver_ = NULL;
    state_ = NOT_CONNECTED;                    /*设置为非连接状态*/
  } else {
    server_address_ = resolver_->address();    /*获取解析后的地址*/
    DoConnect();                               /*连接信令服务器*/
  }
}

域名解析器解析结束后,会回调本函数。如果解析失败,则将PeerConnectionClient设置为NOT_CONNECTED状态;若解析成功,则调用DoConnect()连接信令服务器。

void PeerConnectionClient::DoConnect() 
{
  /*创建异步socket*/
  control_socket_.reset(CreateClientSocket(server_address_.ipaddr().family()));
  hanging_get_.reset(CreateClientSocket(server_address_.ipaddr().family()));

  /*初始化socket的信号与槽*/
  InitSocketSignals();

  /*连接成功后,需要发送的数据。*/
  char buffer[1024];
  snprintf(buffer, sizeof(buffer), "GET /sign_in?%s HTTP/1.0\r\n\r\n", client_name_.c_str());
  onconnect_data_ = buffer;

  /*向信令服务器发送连接请求*/
  bool ret = ConnectControlSocket();
  if (ret)
    state_ = SIGNING_IN;     /*如果连接请求发送成功,标识为SIGNING_IN状态。*/

  if (!ret) {
    callback_->OnServerConnectionFailure();    /*通知Conductor连接失败*/
  }
}

PeerConnectionClient与信令服务器进行交互使用的是http协议,并且是短连接。此处用了两个socket,control_socket_用于主动的向信令服务器发送信令;hanging_get_用于向信令服务器请求信令消息,每次都向信令服务器发送wait信令,等待信令服务器响应,当信令服务器有消息需要发送给客户端时,都会通过这个socket返回。

void PeerConnectionClient::InitSocketSignals() 
{
  RTC_DCHECK(control_socket_.get() != NULL);
  RTC_DCHECK(hanging_get_.get() != NULL);

  /*注册close事件槽函数*/
  control_socket_->SignalCloseEvent.connect(this, &PeerConnectionClient::OnClose);
  hanging_get_->SignalCloseEvent.connect(this, &PeerConnectionClient::OnClose);

  /*注册connect事件槽函数*/
  control_socket_->SignalConnectEvent.connect(this, &PeerConnectionClient::OnConnect);
  hanging_get_->SignalConnectEvent.connect(this, &PeerConnectionClient::OnHangingGetConnect);

  /*注册read事件槽函数*/
  control_socket_->SignalReadEvent.connect(this, &PeerConnectionClient::OnRead);
  hanging_get_->SignalReadEvent.connect(this, &PeerConnectionClient::OnHangingGetRead);
}

PeerConnectionClient使用了异步的socket,所以需要回调函数配合处理。这里使用了信号与槽简化了处理。更多信号与槽相关的知识,可以看我的这篇文章WebRTC源码分析之信号与槽-sigslot

bool PeerConnectionClient::ConnectControlSocket() 
{
  /*检查socket的连接状态*/
  RTC_DCHECK(control_socket_->GetState() == rtc::Socket::CS_CLOSED);

  /*向信令服务器发送连接请求*/
  int err = control_socket_->Connect(server_address_);
  if (err == SOCKET_ERROR) {
    Close();
    return false;
  }

  return true;
}

向信令服务器发送的连接请求是异步的,成功的连接信令服务器后,会触发OnConnect()槽函数。

void PeerConnectionClient::OnConnect(rtc::AsyncSocket* socket) 
{
  RTC_DCHECK(!onconnect_data_.empty());

  /*向信令服务器发送sign in信令*/
  size_t sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length());

  RTC_DCHECK(sent == onconnect_data_.length());
  onconnect_data_.clear();
}
#登录信令服务器发送的登录信息
GET /sign_in?study@LAPTOP-HHU8I2T3 HTTP/1.0

成功连接信令服务器后,接着就是向信令服务器发送登录信息。

#第一个peer登录时,信令服务器对登录信息的响应。
HTTP/1.1 200 Added
Server: PeerConnectionTestServer/0.1
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Content-Length: 26
Pragma: 1
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Content-Length, Connection, Cache-Control
Access-Control-Expose-Headers: Content-Length, X-Peer-Id

study@LAPTOP-HHU8I2T3,1,1

#第二个peer登录时,信令服务器对登录信息的响应。
HTTP/1.1 200 Added
Server: PeerConnectionTestServer/0.1
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Content-Length: 49
Pragma: 2
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Content-Length, Connection, Cache-Control
Access-Control-Expose-Headers: Content-Length, X-Peer-Id

hp@DESKTOP-740K5HL,2,1
study@LAPTOP-HHU8I2T3,1,1

信令服务器接收到Peer的登录信息后,做出的响应。

void PeerConnectionClient::OnRead(rtc::AsyncSocket* socket) 
{
  size_t content_length = 0;
  /*将信令服务器的响应读到control_data_中*/
  if (ReadIntoBuffer(socket, &control_data_, &content_length))
  {
    size_t peer_id = 0, eoh = 0;

    /*验证响应中的状态码,获取peer id。*/
    bool ok = ParseServerResponse(control_data_, content_length, &peer_id, &eoh);
    if (ok)
    {
      /*my_id_是peer的id,是由信令服务器分配的。在没有收到信令服务器分配的id时,其值为-1。*/
      if (my_id_ == -1)
      {
        /*解析数据包得到信令服务器分配的id,并存储到自己的my_id_中。*/
        RTC_DCHECK(state_ == SIGNING_IN);
        my_id_ = static_cast<int>(peer_id);
        RTC_DCHECK(my_id_ != -1);

        /*响应包中除了有服务器分配给本客户端的id,服务器还发送了一个列表,列表中存储着登录服务器的客户端*/
        if (content_length) {   /*如果有http body*/
          /*指向实际的body内容*/
          size_t pos = eoh + 4;

          /*解析body内容*/
          while (pos < control_data_.size()) 
          {
            size_t eol = control_data_.find('\n', pos);
            if (eol == std::string::npos)
              break;

            int id = 0;
            std::string name;
            bool connected;

            /*解析一行数据*/
            if (ParseEntry(control_data_.substr(pos, eol - pos), &name, &id, &connected) && id != my_id_) {  
              /*不是自己的id*/
              peers_[id] = name;     /*保存其他peer的信息*/
              callback_->OnPeerConnected(id, name);  /*通知Conductor其他用户的id和name*/
            }

            /*指向下一行*/
            pos = eol + 1;
          }
        }
        RTC_DCHECK(is_connected());
        callback_->OnSignedIn();     /*通知Conductor登录成功*/
      } else if (state_ == SIGNING_OUT) {
        Close();
        callback_->OnDisconnected();
      } else if (state_ == SIGNING_OUT_WAITING) {
        SignOut();
      }
    }

    control_data_.clear();

    /*如果处于SIGNING_IN,则表示登录成功。*/
    if (state_ == SIGNING_IN) {
      RTC_DCHECK(hanging_get_->GetState() == rtc::Socket::CS_CLOSED);

      state_ = CONNECTED;     /*更新连接状态*/

      /*hanging_get socket连接服务器*/
      hanging_get_->Connect(server_address_);
    }
  }
}

当信令服务器发送登录响应时,会触发PeerConnectionClient::OnRead()函数。

首先从socket读取响应信息至control_data_中,如果是短连接则需要关闭socket。接着验证响应中的状态码,获取信令服务器分配的peer id。

登录信令的响应中会包含其他登录客户端的信息,这些客户端的信令会显示到peer list界面上。解析处其他客户端的信息后,会触发Conductor::OnPeerConnected函数,在这个函数中会将客户端的信息显示到peer list界面上。

hp@DESKTOP-740K5HL,2,1
study@LAPTOP-HHU8I2T3,1,1

响应信息的格式是:peer的name,信令服务器分配的peer id,是否处于登录状态,1表示处于登录状态,0表示登出状态。

成功登录信令服务器后,hanging_get socket也开始登录信令服务器,用于接收信令服务器发送给客户端的信息。

bool PeerConnectionClient::ReadIntoBuffer(rtc::AsyncSocket* socket, std::string* data,size_t* content_length) 
{
  char buffer[0xffff];
  /*从socket中读取网络数据*/
  do {
    int bytes = socket->Recv(buffer, sizeof(buffer), nullptr);    /*读取网络数据*/
    if (bytes <= 0)
      break;
    data->append(buffer, bytes);     /*将读取的网络数据追加到buffer中*/
  } while (true);

  bool ret = false;
  size_t i = data->find("\r\n\r\n"); /*查找http header结束的位置*/
  if (i != std::string::npos) {
    RTC_LOG(INFO) << "Headers received";

    /*从http header中获取Content-Length字段*/
    if (GetHeaderValue(*data, i, "\r\nContent-Length: ", content_length)) {

      /*i指向\r\n\r\n,  + 4 + 内容长度就是http的总大小。*/
      size_t total_response_size = (i + 4) + *content_length;
      if (data->length() >= total_response_size) {
        ret = true;

        std::string should_close;
        const char kConnection[] = "\r\nConnection: ";
        /*查找Connection字段*/
        if (GetHeaderValue(*data, i, kConnection, &should_close) && should_close.compare("close") == 0) 
        {
          //  Connection: close
          /*如果该字段的值为close,表示使用http短连接,那么这次请求完毕了,需要关闭socket。*/
          socket->Close();      /*关闭socket*/
          OnClose(socket, 0);   /*通知关闭socket*/
        }
      } else {
        // We haven't received everything.  Just continue to accept data.
      }
    } else {
      RTC_LOG(LS_ERROR) << "No content length field specified by the server.";
    }
  }

  return ret;
}

从指定的socket读取响应信息,并做适当的处理。如果从响应中得知使用的是http短连接,那么需要关闭socket。

void PeerConnectionClient::OnClose(rtc::AsyncSocket* socket, int err)
{
  RTC_LOG(INFO) << __FUNCTION__;

  socket->Close();      /*关闭socket*/

#ifdef WIN32
  if (err != WSAECONNREFUSED) {      /*不是connect refused错误*/
#else
  if (err != ECONNREFUSED) { 
#endif
    if (socket == hanging_get_.get()) {          /*如果是haning_get_ socket*/
      if (state_ == CONNECTED) {
        hanging_get_->Close();                   /*关闭之前的连接*/
        hanging_get_->Connect(server_address_);  /*建立新的连接*/
      }
    } else {        /*control_get_ socket*/
      callback_->OnMessageSent(err);      /*通知Conductor,消息已经发送。*/
    }
  } else {
    if (socket == control_socket_.get()) {
      RTC_LOG(WARNING) << "Connection refused; retrying in 2 seconds";
      /*如果连接失败,则投递一个异步的任务,2秒之后再次连接。*/
      rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, kReconnectDelay, this, 0);
    } else {
      Close();
      callback_->OnDisconnected();  /*通知Conductor连接断开。*/
    }
  }
}

/*异步任务的回调函数*/
void PeerConnectionClient::OnMessage(rtc::Message* msg) {
  DoConnect();     /*再次重连信令服务器*/
}

主动调用OnClose函数时,err的值为0,如果是hanging_get_,则关闭之前的连接,再建立一个新的连接;若是control_get_,则通知Conductor消息发送完成,可以处理发送其他消息了。

在连接信令服务器时,如果无法连接信令服务器,该函数也会被回调,此时err的值为WSAECONNREFUSED,若是hanging_get_,则关闭程序;如果是control_get_,则会投递一个异步的定时任务,2秒后会再次重连服务器。PeerConnectionClient继承自rtc::MessageHandler接口,当异步任务触发时,会回调OnMessage函数,处理异步的任务,在该函数中会再次连接信令服务器。

bool PeerConnectionClient::ParseServerResponse(const std::string& response, size_t content_length, size_t* peer_id, size_t* eoh) 
{
  /*解析响应中的状态码*/
  int status = GetResponseStatus(response.c_str());
  if (status != 200) {       /*状态码不为200,则表示出错了。*/
    RTC_LOG(LS_ERROR) << "Received error from server";
    Close();
    callback_->OnDisconnected();
    return false;
  }

  *eoh = response.find("\r\n\r\n");       /*指向http header末尾*/
  RTC_DCHECK(*eoh != std::string::npos);
  if (*eoh == std::string::npos)
    return false;

  *peer_id = -1;

  /*Pragma字段用于标识peer id*/
  GetHeaderValue(response, *eoh, "\r\nPragma: ", peer_id);

  return true;
}

该函数用于验证http响应中的状态码,解析并返回信令服务器分配的peer id。

peer_id和eoh传入的是指针,此处作为返回值参数使用。

void PeerConnectionClient::OnHangingGetConnect(rtc::AsyncSocket* socket) 
{
  char buffer[1024];
  snprintf(buffer, sizeof(buffer), "GET /wait?peer_id=%i HTTP/1.0\r\n\r\n", my_id_);
  int len = static_cast<int>(strlen(buffer));

  /*向信令服务器发送wait信令*/
  int sent = socket->Send(buffer, len);

  RTC_DCHECK(sent == len);
}
#向信令服务器发送的wait信令
GET /wait?peer_id=1 HTTP/1.0

hanging_get_登录成功后,会触发该函数,在该函数中会发送wait信令,当信令服务器需要主动向客户端发送消息时,会作为这条信令的响应发送对应的消息。

void PeerConnectionClient::OnHangingGetRead(rtc::AsyncSocket* socket) 
{
  RTC_LOG(INFO) << __FUNCTION__;
  size_t content_length = 0;

  if (ReadIntoBuffer(socket, &notification_data_, &content_length)) {
    size_t peer_id = 0, eoh = 0;     

    /*解析出响应中的peer id*/
    bool ok = ParseServerResponse(notification_data_, content_length, &peer_id, &eoh);

    if (ok) {
      size_t pos = eoh + 4;

      if (my_id_ == static_cast<int>(peer_id)) {   /*其他客户端的登录、登出信令*/
        int id = 0;
        std::string name;
        bool connected = false;
        if (ParseEntry(notification_data_.substr(pos), &name, &id, &connected)){
          if (connected) {        /*有新的客户端登录信令服务器*/
            peers_[id] = name;
            callback_->OnPeerConnected(id, name);   /*更新peer list界面列表*/
          } else {                /*已登录客户端登出信令服务器*/
            peers_.erase(id);
            callback_->OnPeerDisconnected(id);      /*更新peer list界面列表*/
          }
        }
      } else {
        /*信令服务器转发的其他客户端的offer、answer、candidate、bye信息*/
        OnMessageFromPeer(static_cast<int>(peer_id), notification_data_.substr(pos));
      }
    }

    notification_data_.clear();
  }

当信令服务器需要主动发送消息给客户端时,会包装成wait信令的响应信息。有其他客户端登录或登出信令服务器时,会通知本端,本端会根据信令服务器反馈的信息更新peer list界面的用户列表。

当收到信令服务器转发的其他客户端的offer、answer、candidate信息时,会进入OnMessageFromPeer()函数处理。

void PeerConnectionClient::OnMessageFromPeer(int peer_id, const std::string& message)
{
  /*如果是bye信令,表明peer id的peer已经退出了信令服务器,将其从peer list中去掉。*/
  if (message.length() == (sizeof(kByeMessage) - 1) && message.compare(kByeMessage) == 0) {
    callback_->OnPeerDisconnected(peer_id);   /*更新peer list界面用户列表*/
  }  else  {
    /*offer、answer、candidate信息需要发送给Conductor::OnMessageFromPeer()*/
    callback_->OnMessageFromPeer(peer_id, message);
  }
}

开启视频通话

登录信令服务器后,peer list界面会显示所有登录信令服务器的用户。点击任意一个用户就可以开始和此用户建立视频通话。

void Conductor::ConnectToPeer(int peer_id) {
...
    /*创建offer*/
    peer_connection_->CreateOffer(
        this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
...
}

在peer list界面点击用户id会触发至本函数,该函数后面会详细介绍。此函数中很重要的一个操作是调用CreateOffer()函数创建offer。

void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) 
{
...
  std::string sdp;
  desc->ToString(&sdp);     /*将offer转成string*/
    
...
    
  Json::StyledWriter writer;
  Json::Value jmessage;
  jmessage[kSessionDescriptionTypeName] = webrtc::SdpTypeToString(desc->GetType());
  jmessage[kSessionDescriptionSdpName] = sdp;
  
  /*发送offer信令*/
  SendMessage(writer.write(jmessage));
}

WebRTC生成offer后会通过这个回调函数传回offer,在这个函数中会将offer组成json信令,然后调用SendMessage函数发送该信令。

void Conductor::SendMessage(const std::string& json_object) {
  std::string* msg = new std::string(json_object);
  /*向MainWnd投递消息*/
  main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg);
}

此时依然处于WebRTC内部的线程中,而信令的发送必须由主线程完成,所以将待发送的信令投递到MainWnd中,让其发送该信令。后面会再次分析该点。

void Conductor::UIThreadCallback(int msg_id, void* data) 
{
  switch (msg_id) {
...
    case SEND_MESSAGE_TO_PEER: {
      RTC_LOG(INFO) << "SEND_MESSAGE_TO_PEER";
      /*获取消息*/
      std::string* msg = reinterpret_cast<std::string*>(data);   
      if (msg) {
        pending_messages_.push_back(msg);  /*将消息存放到队列中,保证消息有序发送。*/
      }

      /*如果有消息待发送,且PeerConnectionClient可以发送信令。*/
      if (!pending_messages_.empty() && !client_->IsSendingMessage()) {
        msg = pending_messages_.front();    /*从队首获取一条待发送的消息*/
        pending_messages_.pop_front();

        /*通过PeerConnectionClient放该消息*/
        if (!client_->SendToPeer(peer_id_, *msg) && peer_id_ != -1) {
          RTC_LOG(LS_ERROR) << "SendToPeer failed";
          DisconnectFromServer();
        }
        delete msg;
      }

      if (!peer_connection_.get())
        peer_id_ = -1;

      break;
    }
...
}

上面投递了消息后,会在这函数中得到处理,该函数运行在主线程中。

bool PeerConnectionClient::SendToPeer(int peer_id, const std::string& message) 
{
  if (state_ != CONNECTED)
    return false;

  RTC_DCHECK(is_connected());
  RTC_DCHECK(control_socket_->GetState() == rtc::Socket::CS_CLOSED);
  if (!is_connected() || peer_id == -1)
    return false;

  char headers[1024];
  /*将待发送的信令包装到http的body中*/
  snprintf(headers, sizeof(headers),
           "POST /message?peer_id=%i&to=%i HTTP/1.0\r\n"
           "Content-Length: %zu\r\n"
           "Content-Type: text/plain\r\n"
           "\r\n",
           my_id_, peer_id, message.length());

  onconnect_data_ = headers;
  onconnect_data_ += message;

  return ConnectControlSocket();    /*连接信令服务器,发送该请求。*/
}

PeerConnectionClient会将待发送的信令包装到http中,通过http协议发送给信令服务器。

#offer信令
POST /message?peer_id=1&to=2 HTTP/1.0
Content-Length: 4197
Content-Type: text/plain

{
   "sdp" : "v=0\r\no=- 7038993275920826226 ...",
   "type" : "offer"
}

客户端发送的offer信令,peer_id=1&to=2表示该信令需要信令服务器转发给peer id=2的客户端。

#offer信令的响应信息
HTTP/1.1 200 OK
Server: PeerConnectionTestServer/0.1
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Content-Length: 0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Content-Length, Connection, Cache-Control
Access-Control-Expose-Headers: Content-Length, X-Peer-Id

这是offer信令的响应信息

HTTP/1.1 200 OK
Server: PeerConnectionTestServer/0.1
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Content-Length: 4197
Pragma: 1
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Content-Length, Connection, Cache-Control
Access-Control-Expose-Headers: Content-Length, X-Peer-Id

{
   "sdp" : "v=0\r\no=- 7038993275920826226    ...",
   "type" : "offer"
}

信令服务器会根据offer信令中的指示,将该信令转发给peer id=2的客户端。该信息会作为wait信令的响应信息返回。

void PeerConnectionClient::OnHangingGetRead(rtc::AsyncSocket* socket) 
{
...
        OnMessageFromPeer(static_cast<int>(peer_id), notification_data_.substr(pos));
...
}

信令服务器发送wait信令的响应信息时,会触发该函数。

void PeerConnectionClient::OnMessageFromPeer(int peer_id, const std::string& message)
{
...
    callback_->OnMessageFromPeer(peer_id, message);
...
}

最后offer信令会被送至Conductor::OnMessageFromPeer()处理。

answer信令candidate信令在PeerConnectionClient中的处理同offer,这里就不在赘述了。

bool MainWnd::PreTranslateMessage(MSG* msg)
{
...
    } else if (msg->wParam == VK_ESCAPE) {      /*esc键*/
      if (callback_) {
        if (ui_ == STREAMING) {
          callback_->DisconnectFromCurrentPeer(); /*如果在视频界面,则断开当前连接。*/
...
}
          
void Conductor::DisconnectFromCurrentPeer() {
  RTC_LOG(INFO) << __FUNCTION__;
  if (peer_connection_.get()) {
    client_->SendHangUp(peer_id_);     /*发送bye信令*/
    DeletePeerConnection();
  }

  if (main_wnd_->IsWindow())
    main_wnd_->SwitchToPeerList(client_->peers());  /*切换至peer list界面*/
}
          
bool PeerConnectionClient::SendHangUp(int peer_id) 
{
  return SendToPeer(peer_id, kByeMessage);     /*发送bye信令*/
}

在视频通话界面按ESC键,会触发bye信令

登出信令服务器

Condutor可以调用PeerConnectionClient::SignOut()登出信令服务器。

bool PeerConnectionClient::SignOut() 
{
  if (state_ == NOT_CONNECTED || state_ == SIGNING_OUT)
    return true;

  if (hanging_get_->GetState() != rtc::Socket::CS_CLOSED)
    hanging_get_->Close(); /*关闭hangin_get_ socket,不在接收信令服务器主动发送的消息了。*/

  if (control_socket_->GetState() == rtc::Socket::CS_CLOSED) {
    state_ = SIGNING_OUT;  /*更新状态*/

    if (my_id_ != -1) {
      char buffer[1024];
      snprintf(buffer, sizeof(buffer), "GET /sign_out?peer_id=%i HTTP/1.0\r\n\r\n", my_id_);
      onconnect_data_ = buffer;
      return ConnectControlSocket();    /*发送sign out信令*/
    } else {
      return true;
    }
  } else {   /*如果control_socket_ 正在发送信令,则此时无法发送sign out信令。*/
    state_ = SIGNING_OUT_WAITING;    /*标记为等待登出状态*/
  }

  return true;
}

客户端已经需要登出信令服务器了,不再需要信令服务器主动发送的消息,所以关闭了hanging_get_ socket。

如果control_socket_正处于发送消息的过程中,那么此时还不能发送sign out信令,需要等待其处理完毕后,再发送sign out信令。此时先标记为SIGNING_OUT_WAITING状态。

void PeerConnectionClient::OnRead(rtc::AsyncSocket* socket) 
{
...
      } else if (state_ == SIGNING_OUT_WAITING) {
        SignOut();
      }
...
}

control_socket_请求处理完毕后,会检查是否处于SIGNING_OUT_WAITING状态,如果处于此状态,则发送sign out信令。

#sign out信令
GET /sign_out?peer_id=2 HTTP/1.0

#信令服务器对sign out信令的响应
HTTP/1.1 200 OK
Server: PeerConnectionTestServer/0.1
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Content-Length: 0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Content-Length, Connection, Cache-Control
Access-Control-Expose-Headers: Content-Length, X-Peer-Id

客户端登出信令服务器时,发送的sign out信令及其响应。

#信令服务器通知其他所有客户端,有客户端登出了信令服务器。
HTTP/1.1 200 OK
Server: PeerConnectionTestServer/0.1
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Content-Length: 23
Pragma: 1
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Content-Length, Connection, Cache-Control
Access-Control-Expose-Headers: Content-Length, X-Peer-Id

hp@DESKTOP-740K5HL,2,0

一个客户端登出信令服务器后,信令服务器需要通告其他所有的客户端,该客户端登出了服务器。

void PeerConnectionClient::OnHangingGetRead(rtc::AsyncSocket* socket) 
{
...
          if (connected) {     
...
          } else {            /*有客户端登出信令服务器,更新peer list界面用户列表。*/
            peers_.erase(id);
            callback_->OnPeerDisconnected(id);    
          }
...
}

有客户端登出信令服务器后,信令服务器会通过wait信令的响应通知其他在线的客户端。信令服务器发送的客户端登出消息会触发PeerConnectionClient::OnHangingGetRead函数进行处理。

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值