ZLMediaKit源码学习——UDP_udp packet incoming from other thread

UdpServer::createSession我们可以在这里打个断点,接收到UDP包时会调到这边

toolkit::UdpServer::createSession UdpServer.cpp:216
toolkit::UdpServer::getOrCreateSession UdpServer.cpp:210
toolkit::UdpServer::onRead_l UdpServer.cpp:145
toolkit::UdpServer::onRead UdpServer.cpp:129
toolkit::UdpServer::<lambda>::operator()(const toolkit::Buffer::Ptr &, sockaddr \*, int) const UdpServer.cpp:49
std::\_Function\_handler<void (const std::shared_ptr<toolkit::Buffer> &, sockaddr \*, int), <lambda> >::\_M\_invoke(const std::_Any_data &, const std::shared_ptr<toolkit::Buffer> &, sockaddr \*&&, int &&) std_function.h:300
std::function<void (const std::shared_ptr<toolkit::Buffer> &, sockaddr \*, int)>::operator()(const std::shared_ptr<toolkit::Buffer> &, sockaddr \*, int) const std_function.h:688
toolkit::Socket::onRead Socket.cpp:318
toolkit::Socket::<lambda>::operator()(int) const Socket.cpp:259
std::\_Function\_handler<void (int), <lambda> >::\_M\_invoke(const std::_Any_data &, int &&) std_function.h:300
std::function<void (int)>::operator()(int) const std_function.h:688
toolkit::EventPoller::runLoop EventPoller.cpp:304
std::__invoke_impl<void, void (toolkit::EventPoller::\*)(bool, bool), toolkit::EventPoller \*, bool, bool> invoke.h:73
std::__invoke<void (toolkit::EventPoller::\*)(bool, bool), toolkit::EventPoller \*, bool, bool> invoke.h:95
std::thread::_Invoker<std::tuple<void (toolkit::EventPoller::\*)(bool, bool), toolkit::EventPoller \*, bool, bool> >::_M_invoke<0ul, 1ul, 2ul, 3ul> thread:244
std::thread::\_Invoker<std::tuple<void (toolkit::EventPoller::\*)(bool, bool), toolkit::EventPoller \*, bool, bool> >::operator() thread:251
std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (toolkit::EventPoller::\*)(bool, bool), toolkit::EventPoller \*, bool, bool> > >::_M_run thread:195
<unknown> 0x00007ffff7b0ade4
start_thread 0x00007ffff7c1f609
clone 0x00007ffff77f8293

从堆栈我们可以分析出UDP包接收流程如下:

  1. toolkit::EventPoller::runLoop EventPoller.cpp:304:进行事件轮询
  2. toolkit::Socket::::operator()(int) const Socket.cpp:259:触发读事件
  3. toolkit::UdpServer::onRead_l UdpServer.cpp:145: 接收到UDP的处理
void UdpServer::onRead\_l(bool is_server_fd, const UdpServer::PeerIdType &id, const Buffer::Ptr &buf, sockaddr \*addr, int addr_len) {
    // udp server fd收到数据时触发此函数;大部分情况下数据应该在peer fd触发,此函数应该不是热点函数
    bool is_new = false;
    if (auto session = getOrCreateSession(id, buf, addr, addr_len, is_new)) {
        if (session->getPoller()->isCurrentThread()) {
            //当前线程收到数据,直接处理数据
            emitSessionRecv(session, buf);
        } else {
            //数据漂移到其他线程,需要先切换线程
            WarnL << "udp packet incoming from other thread";
            std::weak_ptr<Session> weak_session = session;
            //由于socket读buffer是该线程上所有socket共享复用的,所以不能跨线程使用,必须先拷贝一下
            auto cacheable_buf = std::make\_shared<BufferString>(buf->toString());
            session->async([weak_session, cacheable_buf]() {
                if (auto strong_session = weak_session.lock()) {
                    emitSessionRecv(strong_session, cacheable_buf);
                }
            });
        }

#if !defined(NDEBUG)
        if (!is_new) {
            TraceL << "udp packet incoming from " << (is_server_fd ? "server fd" : "other peer fd");
        }
#endif
    }
}

  1. 判断当前会话是否存在,不存在则创建
const Session::Ptr &UdpServer::getOrCreateSession(const UdpServer::PeerIdType &id, const Buffer::Ptr &buf, sockaddr \*addr, int addr_len, bool &is_new) {
    {
        //减小临界区
        std::lock_guard<std::recursive_mutex> lock(\*_session_mutex);
        auto it = _session_map->find(id);
        if (it != _session_map->end()) {
            return it->second->session();
        }
    }
    is_new = true;
    return createSession(id, buf, addr, addr_len);
}

  1. 创建会话
const Session::Ptr &UdpServer::createSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr \*addr, int addr_len) {
    auto socket = createSocket(_poller, buf, addr, addr_len);
    if (!socket) {
        //创建socket失败,本次onRead事件收到的数据直接丢弃
        return s_null_session;
    }

    auto addr_str = string((char \*) addr, addr_len);
    std::weak_ptr<UdpServer> weak_self = std::dynamic\_pointer\_cast<UdpServer>(shared\_from\_this());
    auto session_creator = [this, weak_self, socket, addr_str, id]() -> const Session::Ptr & {
        auto server = weak_self.lock();
        if (!server) {
            return s_null_session;
        }

        //如果已经创建该客户端对应的UdpSession类,那么直接返回
        lock_guard<std::recursive_mutex> lck(\*_session_mutex);
        auto it = _session_map->find(id);
        if (it != _session_map->end()) {
            return it->second->session();
        }

        socket->bindUdpSock(_socket->get\_local\_port(), _socket->get\_local\_ip());
        socket->bindPeerAddr((struct sockaddr \*) addr_str.data(), addr_str.size());
        //在connect peer后再取消绑定关系, 避免在 server 的 socket 或其他cloned server中收到后续数据包.
        SockUtil::dissolveUdpSock(_socket->rawFD());

        auto helper = \_session\_alloc(server, socket);
        auto session = helper->session();
        // 把本服务器的配置传递给 Session
        session->attachServer(\*this);

        std::weak_ptr<Session> weak_session = session;
        socket->setOnRead([weak_self, weak_session, id](const Buffer::Ptr &buf, struct sockaddr \*addr, int addr_len) {
            auto strong_self = weak_self.lock();
            if (!strong_self) {
                return;
            }

            //快速判断是否为本会话的的数据, 通常应该成立
            if (id == makeSockId(addr, addr_len)) {
                if (auto strong_session = weak_session.lock()) {
                    emitSessionRecv(strong_session, buf);
                }
                return;
            }

            //收到非本peer fd的数据,让server去派发此数据到合适的session对象
            strong_self->onRead\_l(false, id, buf, addr, addr_len);
        });
        socket->setOnErr([weak_self, weak_session, id](const SockException &err) {
            // 在本函数作用域结束时移除会话对象
            // 目的是确保移除会话前执行其 onError 函数
            // 同时避免其 onError 函数抛异常时没有移除会话对象
            onceToken token(nullptr, [&]() {
                // 移除掉会话
                auto strong_self = weak_self.lock();
                if (!strong_self) {
                    return;
                }
                //从共享map中移除本session对象
                lock_guard<std::recursive_mutex> lck(\*strong_self->_session_mutex);
                strong_self->_session_map->erase(id);
            });

            // 获取会话强应用
            if (auto strong_session = weak_session.lock()) {
                // 触发 onError 事件回调
                strong_session->onError(err);
            }
        });

        auto pr = _session_map->emplace(id, std::move(helper));
        assert(pr.second);
        return pr.first->second->session();
    };

    if (socket->getPoller()->isCurrentThread()) {
        //该socket分配在本线程,直接创建session对象,并处理数据
        return session\_creator();
    }

    //该socket分配在其他线程,需要先拷贝buffer,然后在其所在线程创建session对象并处理数据
    auto cacheable_buf = std::make\_shared<BufferString>(buf->toString());
    socket->getPoller()->async([session_creator, cacheable_buf]() {
        //在该socket所在线程创建session对象
        auto session = session\_creator();
        if (session) {
            //该数据不能丢弃,给session对象消费
            emitSessionRecv(session, cacheable_buf);
        }
    });
    return s_null_session;
}

Webrtc数据处理

我们在WebRtcTransport::inputSockData打个断点,此处是webrtc报文的处理的地方:

WebRtcTransport::inputSockData WebRtcTransport.cpp:281
WebRtcSession::onRecv WebRtcSession.cpp:70
toolkit::emitSessionRecv UdpServer.cpp:134
toolkit::UdpServer::<lambda>::operator()(void) const UdpServer.cpp:304
std::\_Function\_handler<void (), <lambda> >::\_M\_invoke(const std::_Any_data &) std_function.h:300
std::function<void ()>::operator()() const std_function.h:688
toolkit::TaskCancelableImp<void ()>::operator()() const TaskExecutor.h:111


![img](https://img-blog.csdnimg.cn/img_convert/aabe0ab61fc48fc2686c61bf5ea0adb5.png)
![img](https://img-blog.csdnimg.cn/img_convert/76e831fc37382e384f1e780b5bfe0552.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
链图片转存中...(img-7pDEjwyf-1725727691016)]
[外链图片转存中...(img-M9mgPIVa-1725727691016)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值