Source code analysis of WebRTC PeerConnection Client 2-peerconnection client

Keywords: webrtc

The version of webrtc analyzed in this paper is m84 platform: win10

Source code analysis of WebRTC PeerConnection Client < 1 > - main window

Source code analysis of WebRTC PeerConnection Client < 2 > - peerconnection client

Source code analysis of WebRTC PeerConnection Client < 3 > - conductor

The packet capture data used in this article can be downloaded here:

PeerConnectionClient is used to interact with the signaling server and acts as a bridge between the Conductor and the signaling server. The signaling interaction sequence diagram is as follows:

Login signaling server

void Conductor::StartLogin(const std::string& server, int port) 
{
  /*If the signaling server has been connected, it returns directly.*/
  if (client_->is_connected())
    return;

  server_ = server;
  client_->Connect(server, port, GetPeerName());      /*Connect signaling server*/
}

After clicking the connect button in the connect interface, the Conductor::StartLogin() function will be triggered. In this function, the PeerConnectionClient::Connect() function will be called to log in to the signaling server.

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

  /*Judge whether the client is connected*/
  if (state_ != NOT_CONNECTED) {
    RTC_LOG(WARNING) << "The client must not be connected before you can call Connect()";

    /*Call Conductor::OnServerConnectionFailure to notify conductor of connection failure.*/
    callback_->OnServerConnectionFailure();
    return;
  }

  /*Judge ip and client_ Is name valid*/
  if (server.empty() || client_name.empty()) {
    callback_->OnServerConnectionFailure();
    return;
  }
  
  /*When the port number is less than 0, the default value is used.*/
  if (port <= 0)
    port = kDefaultServerPort;       /*8888*/

  /*Save ip and port of signaling server*/
  server_address_.SetIP(server);
  server_address_.SetPort(port);

  /*Save client name*/
  client_name_ = client_name;

  /*Determine whether the server address needs to be resolved*/
  if (server_address_.IsUnresolvedIP()){
    state_ = RESOLVING;                     /*Set the status of the client to domain name resolution*/
    resolver_ = new rtc::AsyncResolver();   /*Create an asynchronous domain name parser*/

    /*After the domain name resolution is completed, this processing function will be called back.*/
    resolver_->SignalDone.connect(this, &PeerConnectionClient::OnResolveResult);

    /*Start resolving domain name*/
    resolver_->Start(server_address_);
  } else {     /*If the domain name does not need to be resolved, connect directly to the signaling server.*/ 
    DoConnect();
  }
}

If the signaling server address provided by the user needs to be resolved, an asynchronous domain name parser will be created. Because the domain name resolution is asynchronous, a callback function needs to be registered to receive the resolved results. If you do not need to resolve the address, call DoConnect() to directly connect to the signaling server.

void PeerConnectionClient::OnResolveResult(rtc::AsyncResolverInterface* resolver) 
{
  if (resolver_->GetError() != 0) {            /*No resolution succeeded*/
    callback_->OnServerConnectionFailure();
    resolver_->Destroy(false);
    resolver_ = NULL;
    state_ = NOT_CONNECTED;                    /*Set to non connected state*/
  } else {
    server_address_ = resolver_->address();    /*Get the resolved address*/
    DoConnect();                               /*Connect signaling server*/
  }
}

This function will be called back after the domain name parser finishes parsing. If the resolution fails, set PeerConnectionClient to NOT_CONNECTED status; If the resolution is successful, call DoConnect() to connect to the signaling server.

void PeerConnectionClient::DoConnect() 
{
  /*Create asynchronous socket*/
  control_socket_.reset(CreateClientSocket(server_address_.ipaddr().family()));
  hanging_get_.reset(CreateClientSocket(server_address_.ipaddr().family()));

  /*Signal and slot for initializing socket*/
  InitSocketSignals();

  /*Data to be sent after successful connection.*/
  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;

  /*Send connection request to signaling server*/
  bool ret = ConnectControlSocket();
  if (ret)
    state_ = SIGNING_IN;     /*If the connection request is sent successfully, it is marked as signaling_ In status.*/

  if (!ret) {
    callback_->OnServerConnectionFailure();    /*Notify Conductor of connection failure*/
  }
}

The PeerConnectionClient interacts with the signaling server using the http protocol and is a short connection. Two sockets, control, are used here_ socket_ For actively sending signaling to the signaling server; hanging_get_ It is used to request signaling messages from the signaling server. Each time, it sends wait signaling to the signaling server and waits for the response of the signaling server. When the signaling server has a message to send to the client, it will return through this socket.

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

  /*Register the close event slot function*/
  control_socket_->SignalCloseEvent.connect(this, &PeerConnectionClient::OnClose);
  hanging_get_->SignalCloseEvent.connect(this, &PeerConnectionClient::OnClose);

  /*Register connect event slot function*/
  control_socket_->SignalConnectEvent.connect(this, &PeerConnectionClient::OnConnect);
  hanging_get_->SignalConnectEvent.connect(this, &PeerConnectionClient::OnHangingGetConnect);

  /*Register the read event slot function*/
  control_socket_->SignalReadEvent.connect(this, &PeerConnectionClient::OnRead);
  hanging_get_->SignalReadEvent.connect(this, &PeerConnectionClient::OnHangingGetRead);
}

PeerConnectionClient uses asynchronous socket, so it needs callback function to handle it. Signals and slots are used here to simplify processing. For more information about signals and slots, see my article Signal and slot of WebRTC source code analysis - sigslot.

bool PeerConnectionClient::ConnectControlSocket() 
{
  /*Check the connection status of socket*/
  RTC_DCHECK(control_socket_->GetState() == rtc::Socket::CS_CLOSED);

  /*Send connection request to signaling server*/
  int err = control_socket_->Connect(server_address_);
  if (err == SOCKET_ERROR) {
    Close();
    return false;
  }

  return true;
}

The connection request sent to the signaling server is asynchronous. After successfully connecting to the signaling server, the OnConnect() slot function will be triggered.

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

  /*Send sign in signaling to signaling server*/
  size_t sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length());

  RTC_DCHECK(sent == onconnect_data_.length());
  onconnect_data_.clear();
}
#Login information sent by login signaling server
GET /sign_in?study@LAPTOP-HHU8I2T3 HTTP/1.0

After the signaling server is successfully connected, the login information is sent to the signaling server.

#When the first peer logs in, the signaling server responds to the login information.
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

#When the second peer logs in, the signaling server responds to the login information.
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

The response made by the signaling server after receiving the login information of Peer.

void PeerConnectionClient::OnRead(rtc::AsyncSocket* socket) 
{
  size_t content_length = 0;
  /*Read the response of signaling server to control_data_ in*/
  if (ReadIntoBuffer(socket, &control_data_, &content_length))
  {
    size_t peer_id = 0, eoh = 0;

    /*Verify the status code in the response and obtain the peer id.*/
    bool ok = ParseServerResponse(control_data_, content_length, &peer_id, &eoh);
    if (ok)
    {
      /*my_id_Is the id of the peer, which is assigned by the signaling server. When the id assigned by the signaling server is not received, its value is - 1.*/
      if (my_id_ == -1)
      {
        /*The ID assigned by the signaling server is obtained by parsing the data packet and stored in its own my_id_ Yes.*/
        RTC_DCHECK(state_ == SIGNING_IN);
        my_id_ = static_cast<int>(peer_id);
        RTC_DCHECK(my_id_ != -1);

        /*In the response package, in addition to the id assigned by the server to the client, the server also sends a list in which the client logging in to the server is stored*/
        if (content_length) {   /*If there is http body*/
          /*Point to the actual body content*/
          size_t pos = eoh + 4;

          /*Parsing body content*/
          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;

            /*Parse a row of data*/
            if (ParseEntry(control_data_.substr(pos, eol - pos), &name, &id, &connected) && id != my_id_) {  
              /*Not your own id*/
              peers_[id] = name;     /*Save other peer information*/
              callback_->OnPeerConnected(id, name);  /*Notify Conductor of the id and name of other users*/
            }

            /*Point to the next line*/
            pos = eol + 1;
          }
        }
        RTC_DCHECK(is_connected());
        callback_->OnSignedIn();     /*Notify Conductor of successful login*/
      } else if (state_ == SIGNING_OUT) {
        Close();
        callback_->OnDisconnected();
      } else if (state_ == SIGNING_OUT_WAITING) {
        SignOut();
      }
    }

    control_data_.clear();

    /*If it is in signaling_ In, it indicates that the login is successful.*/
    if (state_ == SIGNING_IN) {
      RTC_DCHECK(hanging_get_->GetState() == rtc::Socket::CS_CLOSED);

      state_ = CONNECTED;     /*Update connection status*/

      /*hanging_get socket Connect server*/
      hanging_get_->Connect(server_address_);
    }
  }
}

When the signaling server sends a login response, the PeerConnectionClient::OnRead() function will be triggered.

First, read the response information from the socket to the control_data_ If it is a short connection, you need to close the socket. Next, verify the status code in the response and obtain the peer id assigned by the signaling server.

The response of login signaling will contain the information of other login clients, and the signaling of these clients will be displayed on the peer list interface. After parsing the information of other clients, the Conductor::OnPeerConnected function will be triggered, in which the information of the client will be displayed on the peer list interface.

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

The format of the response information is: peer name, peer id assigned by the signaling server, and whether it is in login status. 1 means it is in login status, and 0 means it is in logout status.

After successfully logging in to the signaling server, hang_ Get socket also starts to log in to the signaling server to receive the information sent by the signaling server to the client.

bool PeerConnectionClient::ReadIntoBuffer(rtc::AsyncSocket* socket, std::string* data,size_t* content_length) 
{
  char buffer[0xffff];
  /*Read network data from socket*/
  do {
    int bytes = socket->Recv(buffer, sizeof(buffer), nullptr);    /*Read network data*/
    if (bytes <= 0)
      break;
    data->append(buffer, bytes);     /*Append the read network data to the buffer*/
  } while (true);

  bool ret = false;
  size_t i = data->find("\r\n\r\n"); /*Find where http header ends*/
  if (i != std::string::npos) {
    RTC_LOG(INFO) << "Headers received";

    /*Get content length field from http header*/
    if (GetHeaderValue(*data, i, "\r\nContent-Length: ", content_length)) {

      /*i Point to \ r\n\r\n, + 4 + content length is the total size of 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: ";
        /*Find Connection field*/
        if (GetHeaderValue(*data, i, kConnection, &should_close) && should_close.compare("close") == 0) 
        {
          //  Connection: close
          /*If the value of this field is close, indicating that the http short connection is used, then the request is completed and the socket needs to be closed.*/
          socket->Close();      /*Close socket*/
          OnClose(socket, 0);   /*Notify to close 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;
}

Read the response information from the specified socket and handle it appropriately. If you know from the response that you are using an http short connection, you need to close the socket.

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

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

#ifdef WIN32
  if (err != WSAECONNREFUSED) {      /*Not a connect rejected error*/
#else
  if (err != ECONNREFUSED) { 
#endif
    if (socket == hanging_get_.get()) {          /*If it's Hanning_ get_  socket*/
      if (state_ == CONNECTED) {
        hanging_get_->Close();                   /*Close previous connections*/
        hanging_get_->Connect(server_address_);  /*Establish a new connection*/
      }
    } else {        /*control_get_ socket*/
      callback_->OnMessageSent(err);      /*Notify Conductor that the message has been sent.*/
    }
  } else {
    if (socket == control_socket_.get()) {
      RTC_LOG(WARNING) << "Connection refused; retrying in 2 seconds";
      /*If the connection fails, an asynchronous task will be delivered and connected again after 2 seconds.*/
      rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, kReconnectDelay, this, 0);
    } else {
      Close();
      callback_->OnDisconnected();  /*Notifies the Conductor that the connection is disconnected.*/
    }
  }
}

/*Callback function for asynchronous task*/
void PeerConnectionClient::OnMessage(rtc::Message* msg) {
  DoConnect();     /*Reconnect the signaling server again*/
}

When actively calling the OnClose function, the value of err is 0. If it is hanging_get_, Then close the previous connection and establish a new connection; If control_get_, Then the Conductor will be notified that the message sending is completed and other messages can be processed and sent.

When connecting to the signaling server, if the signaling server cannot be connected, the function will also be called back. At this time, the value of err is wsaeconnreused, if it is changing_ get_, Close the program; If it's control_get_, An asynchronous scheduled task will be delivered, and the server will be reconnected in 2 seconds. PeerConnectionClient inherits from the rtc::MessageHandler interface. When an asynchronous task is triggered, it will call back the OnMessage function to process asynchronous tasks. In this function, it will connect to the signaling server again.

bool PeerConnectionClient::ParseServerResponse(const std::string& response, size_t content_length, size_t* peer_id, size_t* eoh) 
{
  /*Parse the status code in the response*/
  int status = GetResponseStatus(response.c_str());
  if (status != 200) {       /*If the status code is not 200, it indicates an error.*/
    RTC_LOG(LS_ERROR) << "Received error from server";
    Close();
    callback_->OnDisconnected();
    return false;
  }

  *eoh = response.find("\r\n\r\n");       /*Point to the end of http header*/
  RTC_DCHECK(*eoh != std::string::npos);
  if (*eoh == std::string::npos)
    return false;

  *peer_id = -1;

  /*Pragma The field identifies the peer id*/
  GetHeaderValue(response, *eoh, "\r\nPragma: ", peer_id);

  return true;
}

This function is used to verify the status code in the http response, parse and return the peer id allocated by the signaling server.

peer_id and eoh pass in pointers, which are used here as return value parameters.

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));

  /*Send wait signaling to signaling server*/
  int sent = socket->Send(buffer, len);

  RTC_DCHECK(sent == len);
}
#wait signaling sent to signaling server
GET /wait?peer_id=1 HTTP/1.0

hanging_get_ After successful login, this function will be triggered. In this function, wait signaling will be sent. When the signaling server needs to actively send a message to the client, it will send the corresponding message as a response to this signaling.

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;     

    /*Resolve the peer id in the response*/
    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)) {   /*Login and logout signaling of other clients*/
        int id = 0;
        std::string name;
        bool connected = false;
        if (ParseEntry(notification_data_.substr(pos), &name, &id, &connected)){
          if (connected) {        /*There is a new client login signaling server*/
            peers_[id] = name;
            callback_->OnPeerConnected(id, name);   /*Update peer list interface list*/
          } else {                /*Logged in client logged out signaling server*/
            peers_.erase(id);
            callback_->OnPeerDisconnected(id);      /*Update peer list interface list*/
          }
        }
      } else {
        /*offer, answer, candidate and bye information of other clients forwarded by signaling server*/
        OnMessageFromPeer(static_cast<int>(peer_id), notification_data_.substr(pos));
      }
    }

    notification_data_.clear();
  }

When the signaling server needs to actively send a message to the client, it will be packaged as the response information of wait signaling. When other clients log in or log out of the signaling server, the client will be notified, and the client will update the user list of the peer list interface according to the information fed back by the signaling server.

When receiving the offer, answer and candidate information of other clients forwarded by the signaling server, it will enter the OnMessageFromPeer() function for processing.

void PeerConnectionClient::OnMessageFromPeer(int peer_id, const std::string& message)
{
  /*If it is bye signaling, it indicates that the peer with peer id has exited the signaling server and is removed from the peer list.*/
  if (message.length() == (sizeof(kByeMessage) - 1) && message.compare(kByeMessage) == 0) {
    callback_->OnPeerDisconnected(peer_id);   /*Update the user list of peer list interface*/
  }  else  {
    /*offer,answer,candidate Information needs to be sent to Conductor::OnMessageFromPeer()*/
    callback_->OnMessageFromPeer(peer_id, message);
  }
}

Turn on video call

After logging in to the signaling server, the peer list interface will display all users logging in to the signaling server. Click any user to start a video call with this user.

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

Clicking the user id in the peer list interface will trigger this function, which will be described in detail later. An important operation in this function is to call the CreateOffer() function to create an offer.

void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) 
{
...
  std::string sdp;
  desc->ToString(&sdp);     /*Convert offer to string*/
    
...
    
  Json::StyledWriter writer;
  Json::Value jmessage;
  jmessage[kSessionDescriptionTypeName] = webrtc::SdpTypeToString(desc->GetType());
  jmessage[kSessionDescriptionSdpName] = sdp;
  
  /*Send offer signaling*/
  SendMessage(writer.write(jmessage));
}

After WebRTC generates offer, it returns offer through this callback function. In this function, offer will constitute json signaling and then call the SendMessage function to send the signalling.

void Conductor::SendMessage(const std::string& json_object) {
  std::string* msg = new std::string(json_object);
  /*Post message to MainWnd*/
  main_wnd_->QueueUIThreadCallback(SEND_MESSAGE_TO_PEER, msg);
}

At this time, it is still in the thread inside WebRTC, and the signaling must be sent by the main thread, so the signaling to be sent is delivered to MainWnd to send the signaling. This point will be analyzed again later.

void Conductor::UIThreadCallback(int msg_id, void* data) 
{
  switch (msg_id) {
...
    case SEND_MESSAGE_TO_PEER: {
      RTC_LOG(INFO) << "SEND_MESSAGE_TO_PEER";
      /*Get message*/
      std::string* msg = reinterpret_cast<std::string*>(data);   
      if (msg) {
        pending_messages_.push_back(msg);  /*Store the message in the queue to ensure that the message is sent in order.*/
      }

      /*If there is a message to be sent, and the PeerConnectionClient can send signaling.*/
      if (!pending_messages_.empty() && !client_->IsSendingMessage()) {
        msg = pending_messages_.front();    /*Get a message to be sent from the head of the queue*/
        pending_messages_.pop_front();

        /*Put the message through 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;
    }
...
}

After the above message is delivered, it will be processed in this function, which runs in the main thread.

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];
  /*Wrap the signaling to be sent into the body of http*/
  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();    /*Connect to the signaling server and send the request.*/
}

PeerConnectionClient will package the signaling to be sent into http and send it to the signaling server through http protocol.

#offer signaling
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 signaling sent by the client, peer_ Id = 1 & to = 2 indicates that the signaling needs to be forwarded by the signaling server to the client with peer id=2.

#Response information of offer signaling
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

This is the response information of offer signaling

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"
}

The signaling server will forward the offer signaling to the client with peer id=2 according to the instructions in the offer signaling. This information will be returned as the response information of wait signaling.

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

This function will be triggered when the signaling server sends the response information of wait signaling.

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

Finally, the offer signaling will be sent to Conductor::OnMessageFromPeer() for processing.

The processing of answer signaling and candidate signaling in PeerConnectionClient is the same as that of offer, which will not be repeated here.

bool MainWnd::PreTranslateMessage(MSG* msg)
{
...
    } else if (msg->wParam == VK_ESCAPE) {      /*esc key*/
      if (callback_) {
        if (ui_ == STREAMING) {
          callback_->DisconnectFromCurrentPeer(); /*If you are in the video interface, disconnect the current connection.*/
...
}
          
void Conductor::DisconnectFromCurrentPeer() {
  RTC_LOG(INFO) << __FUNCTION__;
  if (peer_connection_.get()) {
    client_->SendHangUp(peer_id_);     /*Send bye signaling*/
    DeletePeerConnection();
  }

  if (main_wnd_->IsWindow())
    main_wnd_->SwitchToPeerList(client_->peers());  /*Switch to the peer list interface*/
}
          
bool PeerConnectionClient::SendHangUp(int peer_id) 
{
  return SendToPeer(peer_id, kByeMessage);     /*Send bye signaling*/
}

Pressing ESC on the video call interface will trigger bye signaling.

Logout signaling server

The conductor can call PeerConnectionClient::SignOut() to log out of the signaling server.

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

  if (hanging_get_->GetState() != rtc::Socket::CS_CLOSED)
    hanging_get_->Close(); /*Close hangin_get_ socket. It is no longer receiving the message actively sent by the signaling server.*/

  if (control_socket_->GetState() == rtc::Socket::CS_CLOSED) {
    state_ = SIGNING_OUT;  /*Update status*/

    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();    /*Send sign out signaling*/
    } else {
      return true;
    }
  } else {   /*If control_socket_  If signaling is being sent, sign out signaling cannot be sent at this time.*/
    state_ = SIGNING_OUT_WAITING;    /*Mark as pending logout*/
  }

  return true;
}

The client already needs to log out of the signaling server. It no longer needs the messages actively sent by the signaling server, so hanging is turned off_ get_ socket.

If control_socket_ If you are in the process of sending messages, you cannot send sign out signaling at this time. You need to wait until it is processed before sending sign out signaling. At this time, it is marked as signing first_ OUT_ Waiting status.

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

control_socket_ After the request is processed, it will check whether it is in SIGNING_OUT_WAITING state. If it is in this state, sign out signaling will be sent.

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

#Response of signaling server to sign out signaling
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

When the client logs out of the signaling server, it sends the sign out signaling and its response.

#The signaling server notifies all other clients that a client has logged out of the signaling server.
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

After a client logs out of the signaling server, the signaling server needs to notify all other clients that the client logs out of the server.

void PeerConnectionClient::OnHangingGetRead(rtc::AsyncSocket* socket) 
{
...
          if (connected) {     
...
          } else {            /*A client logs out of the signaling server and updates the user list of the peer list interface.*/
            peers_.erase(id);
            callback_->OnPeerDisconnected(id);    
          }
...
}

After a client logs out of the signaling server, the signaling server will notify other online clients through the response of wait signaling. The client logout message sent by the signaling server will trigger the PeerConnectionClient::OnHangingGetRead function for processing.

Posted by Ironphp on Sat, 02 Oct 2021 14:09:57 -0700