webrtc 中有关 socket 运行机制以及 stun 收发过程 及 Candidates 生成流程分析

本文深入分析了WebRTC中的STUN机制,包括socket运行机制、STUN收发过程及Candidates生成流程。同时,介绍了如何在一分钟内快速搭建RTMP服务器,提供了软件下载地址和GitHub项目链接。

----------------------------------------------------------------------------------------------------------------------------------------

一分钟快速搭建 rtmpd 服务器: https://blog.csdn.net/freeabc/article/details/102880984

软件下载地址: http://www.qiyicc.com/download/rtmpd.rar

github 地址:https://github.com/superconvert/smart_rtmpd

-----------------------------------------------------------------------------------------------------------------------------------------

webrtc 中有关 socket 运行机制以及 stun 收发过程 及 Candidates 生成流程分析

我写文章一般是两个思路:
1. 下一步要调用什么对象的方法
2.  这一步的对象,怎么关联到下一步的对象的流程分析
这一步的流程主要阐述怎么关联下一步的对象的流程分析,当然这一步做了什么具体的工作,不能
详细展示,否则,太庞大了,需要各位朋友针对重点的部分,自己揣摩了。

//*******************************************************************************************
//
// webrtc 内部很多创建 socket 的地方,这个需要调用类厂 BasicPacketSocketFactory , 下面
// 这一小段就是分析 BasicPacketSocketFactory 的创建,以及内部管理的 socket 的部分流程
//
//*******************************************************************************************

AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket(
    const SocketAddress& address,
    uint16_t min_port,
    uint16_t max_port) {

    // 参见下面的 SocketDispatcher
    AsyncSocket* socket =
        socket_factory()->CreateAsyncSocket(address.family(), SOCK_DGRAM);
    if (!socket) {
        return NULL;
    }

    // 这个 BindSocket 最终会调用系统的 bind
    if (BindSocket(socket, address, min_port, max_port) < 0) {
        RTC_LOG(LS_ERROR) << "UDP bind failed with error " << socket->GetError();
        delete socket;
        return NULL;
    }

    // 这个里面绑定了读和写事件到 AsyncUDPSocket::OnReadEvent , AsyncUDPSocket::OnWriteEvent
    return new AsyncUDPSocket(socket);
}

    1. 创建 BasicPacketSocketFactory
    ./pc/peer_connection_factory.cc
    BasicPacketSocketFactory 是 PeerConnectionFactory::Initialize() 中创建的

default_socket_factory_.reset(new rtc::BasicPacketSocketFactory(network_thread_));

    2.
    ./sdk/android/src/jni/pc/peer_connection_factory.cc
    而 network_thread_ 则是 接口 CreatePeerConnectionFactoryForJava 里的
    std::unique_ptr<rtc::Thread> network_thread = rtc::Thread::CreateWithSocketServer();    
    其实就是这个

std::unique_ptr<Thread> Thread::CreateWithSocketServer() {
    return std::unique_ptr<Thread>(new Thread(SocketServer::CreateDefault()));
}

    其实就是创建了 PhysicalSocketServer

std::unique_ptr<SocketServer> SocketServer::CreateDefault() {
    #if defined(__native_client__)
        return std::unique_ptr<SocketServer>(new rtc::NullSocketServer);
    #else
        return std::unique_ptr<SocketServer>(new rtc::PhysicalSocketServer);
    #endif
}

   Thread 继承于 class Thread : public MessageQueue, public webrtc::TaskQueueBase ,我们看出 Thread 拥有消息队列的对象
    构造函数 Thread(SocketServer* ss)把 ss 赋值给基类 MessageQueue,基类接口通过 socketserver 返回这个对象,后续只要
    调用接口 socketserver 的,就是返回这个 socket 对象。

SocketServer* MessageQueue::socketserver() {
    return ss_;
}

    上面的 BasicPacketSocketFactory::CreateUdpSocket 里的,这句话 socket_factory()->CreateAsyncSocket 其实就是调用 
    ./rtc_base/physical_socket_server.cc

AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int family, int type) {       
    SocketDispatcher* dispatcher = new SocketDispatcher(this);
    // 这个里面通过 PhysicalSocket::Create 创建一个套接字
    if (dispatcher->Create(family, type)) {
        return dispatcher;
    } else {
        delete dispatcher;
        return nullptr;
    }
}

//******************************************************************************
//
// 下面这段是讲述 socket 怎么接收数据的,和上述流程没任何关系
//
//******************************************************************************

    上述流程中,有一个这个函数调用,

std::unique_ptr<Thread> Thread::CreateWithSocketServer() {
    return std::unique_ptr<Thread>(new Thread(SocketServer::CreateDefault()));
}

    创建一个带线程的 socket    这个线程的 Run 如下:

void Thread::Run() {
    ProcessMessages(kForever);
}

    // 这个里面不断的 Get 最新的 message 进行处理

bool Thread::ProcessMessages(int cmsLoop) {
    // Using ProcessMessages with a custom clock for testing and a time greater
    // than 0 doesn't work, since it's not guaranteed to advance the custom
    // clock's time, and may get stuck in an infinite loop.
    RTC_DCHECK(GetClockForTesting() == nullptr || cmsLoop == 0 ||
        cmsLoop == kForever);
    int64_t msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop);
    int cmsNext = cmsLoop;

    while (true) {
  #if defined(WEBRTC_MAC)
      ScopedAutoReleasePool pool;
  #endif
      Message msg;
      if (!Get(&msg, cmsNext))
          return !IsQuitting();
      Dispatch(&msg);

      if (cmsLoop != kForever) {
          cmsNext = static_cast<int>(TimeUntil(msEnd));
          if (cmsNext < 0)
            return true;
      }
    }
}

   // 其实就是基类的 MessageQueue 的接口

bool MessageQueue::Get(Message* pmsg, int cmsWait, bool process_io)
    // 看到这个 ss_ 了吗,就是 SocketServer::CreateDefault() 也就是 PhysicalSocketServer::Wait 接口
    if (!ss_->Wait(static_cast<int>(cmsNext), process_io))

    这个地方监听所有的 socket 操作,三个版本的都有 win, linux,随便找一个分析
    ./rtc_base/physical_socket_server.cc

bool PhysicalSocketServer::Wait(int cmsWait, bool process_io)
    return WaitEpoll(cmsWait, signal_wakeup_);

bool PhysicalSocketServer::WaitEpoll(int cmsWait)
    ProcessEvents(pdispatcher, readable, writable, check_error);

static void ProcessEvents(Dispatcher* dispatcher, bool readable, bool writable, bool check_error) 
    // 这里就是 SocketDispatcher -
    dispatcher->OnEvent(ff, errcode);

void SocketDispatcher::OnEvent(uint32_t ff, int err)
    // 如果是读,这里假设是 UDP 
    SignalReadEvent(this);

    ./rtc_base/async_udp_socket.cc

void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket)
    SignalReadPacket(this, buf_, static_cast<size_t>(len), remote_addr,
        (timestamp > -1 ? timestamp : TimeMicros()));

    ./p2p/base/stun_port.cc 

void UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
    const char* data,
    size_t size,
    const rtc::SocketAddress& remote_addr,
    const int64_t& packet_time_us) {    
    RTC_DCHECK(socket == socket_);
    RTC_DCHECK(!remote_addr.IsUnresolvedIP());

    // Look for a response from the STUN server.
    // Even if the response doesn't match one of our outstanding requests, we
    // will eat it because it might be a response to a retransmitted packet, and
    // we already cleared the request when we got the first response.        
    if (server_addresses_.find(remote_addr) != server_addresses_.end()) {
        // 这是 stun 阶段接收包
        requests_.CheckResponse(data, size);
        return;
    }

    // 这是建立链接后接收包,参考 webrtc 的视频数据接收过程
    if (Connection* conn = GetConnection(remote_addr)) {
        conn->OnReadPacket(data, size, packet_time_us);
    } else {
        Port::OnReadPacket(data, size, remote_addr, PROTO_UDP);
    }
}

//******************************************************************************
//
// 下面就分析了有关 webrtc stun 流程的部分
//
//******************************************************************************
    
1. 从这里开始分析,这个的调用参考 createPeerConnection 流程
JsepTransportController::MaybeStartGathering

2. 这个 ice_transport 就是 P2PTransportChannel 对象
dtls->ice_transport()->MaybeStartGathering();

3. 第一次创建流程
./p2p/base/p2p_transport_channel.cc

P2PTransportChannel::MaybeStartGathering 
    //------------------------------------------------------------
    // 这个就是创建一个 PortAllocatorSession 并把信号挂接 P2PTransportChannel
    //------------------------------------------------------------
    AddAllocatorSession(allocator_->CreateSession(
          transport_name(),
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值