libjingle : SessionManager 分析

        此篇主要分析SessionManager的两线程:signaling_thread_和worker_thread_。

        以目前笔者对SessionManager的理解,signaling_thread_主要负责信号处理,而worker_thread_主要负责处理NAT穿透和直连通道的建立。

        worker_thread_可以通过构造SessionManager实例的时候指定一个线程,如果不指定,则为构造SessionManager对象的时候所处的当前线程;而sigaling_thread_则一定是构造SessionManager对象的时候所处的当前线程。代码如下:
    signaling_thread_ = talk_base::Thread::Current();
    if (worker == NULL) {
        worker_thread_ = talk_base::Thread::Current();
    } else {
        worker_thread_ = worker;
    }
        通过SessionManager创建Session的时候,必须保证Session的sigaling_thread_与SessionManager中的signaling_thread保持一致。代码如下:
 
Session::Session(.... )
    : BaseSession( session_manager->signaling_thread() ,
                  session_manager->worker_thread(),
                  session_manager->port_allocator(),
                  sid, content_type, initiator_name == local_name) {
  .....
}

BaseSession::BaseSession( talk_base::Thread* signaling_thread ,
                         talk_base::Thread* worker_thread,
                         PortAllocator* port_allocator,
                         const std::string& sid,
                         const std::string& content_type,
                         bool initiator)
 {
   ASSERT(signaling_thread->IsCurrent());
}
        
        在直连通道创建成功后,可以通过直连通道相互发送数据,数据发送是通过PseudoTcpChannel::Write接口发送,最终由P2PTransportChannel::SendPacket接口发送给对方。
        通过PseudoTcpChannel::Write发送的时候判断了stream_thread_是否为当前线程,据笔者跟踪代码所致,如果是被动创建,stream_thread_将由

<<libjingle sessionmanagertask 分析>>中XmppPump中的m_pXmppThread所决定。如图:

libjingle : SessionManager 分析_第1张图片

跟踪代码,很容易知道stream_thread_最终由TunnelSessionClientBase::OnSessionCreate确定,代码如下:
void TunnelSessionClientBase::OnSessionCreate(Session* session, bool received) {
  LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionCreate: received=" 
               << received;
  ASSERT(session_manager_->signaling_thread()->IsCurrent());
  if (received)
    sessions_.push_back(
        MakeTunnelSession(session,  talk_base::Thread::Current() , RESPONDER));
}

    由此可知,如果是被动创建Session,stream_thread_为XmppPump中的m_pXmppThread。但是PseudoTcpChannel中的sigaling_thread_必须保证和SessionManageer中的sigaling_thread_保存一致。

    如果是主动创建Session呢?我们看看堆栈信息,如图:
libjingle : SessionManager 分析_第2张图片
跟踪源代码,很容易知道是因为调用了TunnelSessionClientBase::CreateTunnel接口,代码如下:
talk_base::StreamInterface* TunnelSessionClientBase::CreateTunnel(
    const buzz::Jid& to, const std::string& description) {
  // Valid from any thread
  CreateTunnelData data;
  data.jid = to;
  data.description = description;
   data.thread = talk_base::Thread::Current(); //这里就是最终要赋值给 PseudoTcpChannel的stream_thread_
  data.stream = NULL;
   session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
  return data.stream;
}

void TunnelSessionClientBase::OnMessage(talk_base::Message* pmsg) {
  if (pmsg->message_id ==  MSG_CREATE_TUNNEL ) {
    ASSERT(session_manager_->signaling_thread()->IsCurrent());
    CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
    SessionDescription* offer = CreateOffer(data->jid, data->description);
    if (offer == NULL) {
      return;
    }

    Session* session = session_manager_->CreateSession(jid_.Str(), namespace_);
    TunnelSession* tunnel = MakeTunnelSession(session,  data->thread ,
                                              INITIATOR);
    sessions_.push_back(tunnel);
    session->Initiate(data->jid.Str(), offer);
    data->stream = tunnel->GetStream();
  }
}
        所以我们可以肯定,如果是主动创建Session,则PseudoTcpChannel的stream_thread_则由调用TunnelSessionClientBase::CreateTunnel接口的线程所决定。但是PseudoTcpChannel中的sigaling_thread_必须保证和SessionManageer中的sigaling_thread_保存一致。

        因此在调用PseudoTcpChannel::Write接口发送消息的时候,要确保调用接口的线程和stream_thread_的保存一致。

        我们在上面提到过,PseudoTcpChannel::Write接口最终是要调用P2PTransportChannel::SendPacket,在调用接口实现里,判断了worker_thread_必须为当前线程,也就是说SesisonManager中的worker_thread_线程必须为当前线程,即PseudoTcpChannel::Write接口调用的当前线程为SesisonManager::worker_thread_。代码如下:
P2PTransportChannel::P2PTransportChannel(.... )
: ........
    transport_(transport),
    allocator_(allocator),
     worker_thread_(talk_base::Thread::Current()),
{
}

int P2PTransportChannel::SendPacket(const char *data, size_t len, int flags) {
   ASSERT(worker_thread_ == talk_base::Thread::Current());
  if (flags != 0) {
    error_ = EINVAL;
    return -1;
  }
  if (best_connection_ == NULL) {
    error_ = EWOULDBLOCK;
    return -1;
  }
  int sent = best_connection_->Send(data, len);
  if (sent <= 0) {
    ASSERT(sent < 0);
    error_ = best_connection_->GetError();
  }
  return sent;
}
        跟踪函数我们可知,P2PTransportChannel的worker_thread_是由SessionManager中的worker_thread_确定,如图:
libjingle : SessionManager 分析_第3张图片
 以下为关键代码:
TransportChannelImpl* Transport::CreateChannel(int component) {
  ChannelParams params(component);
   worker_thread()->Send(this, MSG_CREATECHANNEL, &params);//这里的workthread()是由SessionMananger的worker_thread_决定
  return params.channel;
}

void Transport::OnMessage(talk_base::Message* msg) {
  switch (msg->message_id) {
    case MSG_CREATECHANNEL: {
        ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
        params->channel =  CreateChannel_w(params->component) ;
      }
..........................
}

TransportChannelImpl*  Transport::CreateChannel_w (int component) {
  ASSERT(worker_thread()->IsCurrent());
  TransportChannelImpl *impl;
  talk_base::CritScope cs(&crit_);

  // Create the entry if it does not exist.
  bool impl_exists = false;
  if (channels_.find(component) == channels_.end()) {
    impl =  CreateTransportChannel(component);
    channels_[component] = ChannelMapEntry(impl);
  } else {
    impl = channels_[component].get();
    impl_exists = true;
  }
...................................
}

virtual DtlsTransportChannelWrapper*  DtlsTransport::CreateTransportChannel(int component)  {
    return new DtlsTransportChannelWrapper(
        this,  Base::CreateTransportChannel (component));
  }

TransportChannelImpl*  P2PTransport::CreateTransportChannel (int component) {
  return  new P2PTransportChannel (
      content_name(), component, this, port_allocator());
}

        因此我们应该可以确定,发送数据的线程必须和SessinManager中的worker_thread_线程保持一致????!!!
        如果是这样的话,那么怎么分开SessionManger中的sigaling_thread_和worker_thread_???????

你可能感兴趣的:(gtalk,libjingle)