一文详解OpenHarmony软总线

描述

本次说明可能侧重在标准系统之上。软总线依旧采用鸿蒙经典的 proxy - stub 架构,接口类 ISoftBusServer,ISoftBusClient。

 

一般来说,一些服务就一个接口类,为什么软总线会有两个呢?我们再看看继承关系。

 和 ISoftBusServer 相关的有:

架构

类似的 ISoftBusClient:

架构

从上面的图中可以看出,一个 stub 甚至对应几个 proxy,看下代码,可以看到就是 proxy 就是解耦,更加的职责清晰。

 

我们通过观察目录结构和对应的代码接口进行查看,便不难看出一二。

 先看 ISoftBusClient 接口类:
namespace OHOS {
class ISoftBusClient : public IRemoteBroker {
public:
    virtual ~ISoftBusClient() = default;

    virtual void OnDeviceFound(const DeviceInfo *device) 0;
    virtual void OnDiscoverFailed(int subscribeId, int failReason) 0;
    virtual void OnDiscoverySuccess(int subscribeId) 0;
    virtual void OnPublishSuccess(int publishId) 0;
    virtual void OnPublishFail(int publishId, int reason) 0;
    virtual int32_t OnChannelOpened(const char *sessionName, const ChannelInfo *channel) 0;
    virtual int32_t OnChannelOpenFailed(int32_t channelId, int32_t channelType) 0;
    virtual int32_t OnChannelLinkDown(const char *networkId, int32_t routeType) 0;
    virtual int32_t OnChannelMsgReceived(int32_t channelId, int32_t channelType, const void *data,
        uint32_t len, int32_t type) 0;
    virtual int32_t OnChannelClosed(int32_t channelId, int32_t channelType) 0;
    virtual int32_t OnChannelQosEvent(int32_t channelId, int32_t channelType, int32_t eventId, int32_t tvCount,
        const QosTv *tvList) 0;
    virtual int32_t OnJoinLNNResult(void *addr, uint32_t addrTypeLen, const char *networkId, int retCode) 0;
    virtual int32_t OnLeaveLNNResult(const char *networkId, int retCode) 0;
    virtual int32_t OnNodeOnlineStateChanged(bool isOnline, void *info, uint32_t infoTypeLen) 0;
    virtual int32_t OnNodeBasicInfoChanged(void *info, uint32_t infoTypeLen, int32_t type) 0;
    virtual int32_t OnTimeSyncResult(const void *info, uint32_t infoTypeLen, int32_t retCode) 0;
    virtual void OnPublishLNNResult(int32_t publishId, int32_t reason);
    virtual void OnRefreshLNNResult(int32_t refreshId, int32_t reason);
    virtual void OnRefreshDeviceFound(const void *device, uint32_t deviceLen);

public:
    DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.ISoftBusClient");
};
} // namespace OHOS
 其中的接口方法就是主要的 SDK 中的对外接口。

 

再看 ISoftBusServer:

namespace OHOS {
class ISoftBusServer : public IRemoteBroker {
public:
    virtual ~ISoftBusServer() = default;

    virtual int32_t StartDiscovery(const char *pkgName, const SubscribeInfo *info) 0;
    virtual int32_t StopDiscovery(const char *pkgName, int subscribeId) 0;
    virtual int32_t PublishService(const char *pkgName, const PublishInfo *info) 0;
    virtual int32_t UnPublishService(const char *pkgName, int publishId) 0;
    virtual int32_t SoftbusRegisterService(const char *clientPkgName, const sptr &object) 0;

    virtual int32_t CreateSessionServer(const char *pkgName, const char *sessionName) 0;
    virtual int32_t RemoveSessionServer(const char *pkgName, const char *sessionName) 0;
    virtual int32_t OpenSession(const SessionParam *param, TransInfo *info) 0;
    virtual int32_t OpenAuthSession(const char *sessionName, const ConnectionAddr *addrInfo) 0;
    virtual int32_t NotifyAuthSuccess(int channelId) 0;
    virtual int32_t CloseChannel(int32_t channelId, int32_t channelType) 0;
    virtual int32_t SendMessage(int32_t channelId, int32_t channelType,
        const void *data, uint32_t len, int32_t msgType) 0;
    virtual int32_t JoinLNN(const char *pkgName, void *addr, uint32_t addrTypeLen) 0;
    virtual int32_t LeaveLNN(const char *pkgName, const char *networkId) 0;
    virtual int32_t GetAllOnlineNodeInfo(const char *pkgName, void **info, uint32_t infoTypeLen, int *infoNum) 0;
    virtual int32_t GetLocalDeviceInfo(const char *pkgName, void *info, uint32_t infoTypeLen) 0;
    virtual int32_t GetNodeKeyInfo(const char *pkgName, const char *networkId, int key, unsigned char *buf,
        uint32_t len) 0;
    virtual int32_t StartTimeSync(const char *pkgName, const char *targetNetworkId, int32_t accuracy,
        int32_t period) 0;
    virtual int32_t StopTimeSync(const char *pkgName, const char *targetNetworkId) 0;
    virtual int32_t QosReport(int32_t channelId, int32_t chanType, int32_t appType, int32_t quality) 0;
    virtual int32_t PublishLNN(const char *pkgName, const void *info, uint32_t infoTypeLen);
    virtual int32_t StopPublishLNN(const char *pkgName, int32_t publishId);
    virtual int32_t RefreshLNN(const char *pkgName, const void *info, uint32_t infoTypeLen);
    virtual int32_t StopRefreshLNN(const char *pkgName, int32_t refreshId);
    virtual int32_t ActiveMetaNode(const MetaNodeConfigInfo *info, char *metaNodeId);
    virtual int32_t DeactiveMetaNode(const char *metaNodeId);
    virtual int32_t GetAllMetaNodeInfo(MetaNodeInfo *info, int32_t *infoNum);

public:
    DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.ISoftBusServer");
};

 

包括发现设备,发布服务,相当于这是系统自启动的一个服务。  

解析一次调用链

 

 

对于 proxy - client 架构,一般来说就是 client 调用 sendRequest,server 便会调用 OnRemoteRequest。

 

我们直接从 stub 的方法入手分析下:

SoftBusClientStub::SoftBusClientStub()
{
    memberFuncMap_[CLIENT_DISCOVERY_DEVICE_FOUND] =
        &SoftBusClientStub::OnDeviceFoundInner;
    memberFuncMap_[CLIENT_DISCOVERY_SUCC] =
        &SoftBusClientStub::OnDiscoverySuccessInner;
    memberFuncMap_[CLIENT_DISCOVERY_FAIL] =
        &SoftBusClientStub::OnDiscoverFailedInner;
    memberFuncMap_[CLIENT_PUBLISH_SUCC] =
        &SoftBusClientStub::OnPublishSuccessInner;
    memberFuncMap_[CLIENT_PUBLISH_FAIL] =
        &SoftBusClientStub::OnPublishFailInner;
    memberFuncMap_[CLIENT_ON_CHANNEL_OPENED] =
        &SoftBusClientStub::OnChannelOpenedInner;
    memberFuncMap_[CLIENT_ON_CHANNEL_OPENFAILED] =
        &SoftBusClientStub::OnChannelOpenFailedInner;
    memberFuncMap_[CLIENT_ON_CHANNEL_LINKDOWN] =
        &SoftBusClientStub::OnChannelLinkDownInner;
    memberFuncMap_[CLIENT_ON_CHANNEL_CLOSED] =
        &SoftBusClientStub::OnChannelClosedInner;
    memberFuncMap_[CLIENT_ON_CHANNEL_MSGRECEIVED] =
        &SoftBusClientStub::OnChannelMsgReceivedInner;
    memberFuncMap_[CLIENT_ON_CHANNEL_QOSEVENT] =

 

这里我们看到是使用不同的 CODE 做分发。但是对外的接口都是 c 接口,c++ 接口中没有任何内容存储信息。这是为啥?这是为了兼容标准系统和其他系统。

 

信息存储再统一的结构里面,然后根据不同的系统编译不同的 .c 或者 .cpp 文件。

 

咱们以 joinLNN 为例:

int32_t JoinLNN(const char *pkgName, ConnectionAddr *target, OnJoinLNNResult cb)
{
    if (pkgName == NULL || target == NULL || cb == NULL) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : params are NULL!");
        return SOFTBUS_INVALID_PARAM;
    }
    if (CommonInit(pkgName) != SOFTBUS_OK) {
        return SOFTBUS_INVALID_PARAM;
    }
    return JoinLNNInner(pkgName, target, cb);
}

 

实际调用的是 joinLNNInner:

int32_t JoinLNNInner(const char *pkgName, ConnectionAddr *target, OnJoinLNNResult cb)
{
    int32_t rc;

    if (!g_busCenterClient.isInit) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : join lnn not init");
        return SOFTBUS_NO_INIT;
    }
    if (SoftBusMutexLock(&g_busCenterClient.lock) != 0) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail: lock join lnn cb list in join");
    }
    rc = SOFTBUS_ERR;
    do {
        if (FindJoinLNNCbItem(target, cb) != NULL) {
            SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : join request already exist");
            rc = SOFTBUS_ALREADY_EXISTED;
            break;
        }
        rc = ServerIpcJoinLNN(pkgName, target, sizeof(*target));
        if (rc != SOFTBUS_OK) {
            SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : request join lnn");
        } else {
            rc = AddJoinLNNCbItem(target, cb);
        }
    } while (false);
    if (SoftBusMutexUnlock(&g_busCenterClient.lock) != 0) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail: unlock join lnn cb list in join");
    }
    return rc;
}

 

先做了一些初始化的操作,查找当前节点是否存在。然后 ServerIpcJoinLNN 通信就是使用的 proxy-stub 侧的代码。

int32_t ServerIpcJoinLNN(const char *pkgName, void *addr, unsigned int addrTypeLen)
{
    if (g_serverProxy == nullptr) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "ServerIpcJoinLNN g_serverProxy is nullptr!
");
        return SOFTBUS_ERR;
    }
    int ret = g_serverProxy->JoinLNN(pkgName, addr, addrTypeLen);
    if (ret != 0) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "ServerIpcJoinLNN failed!
");
        return ret;
    }
    return SOFTBUS_OK;
}

 

这里的关键就是 g_serverProxy->JoinLNN(pkgName,addr,addrTypeLen);

 

实际调用的是:
int32_t BusCenterServerProxy::JoinLNN(const char *pkgName, void *addr, uint32_t addrTypeLen)
{
    if (pkgName == nullptr || addr == nullptr) {
        return SOFTBUS_ERR;
    }
    sptr remote = GetSystemAbility();
    if (remote == nullptr) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "remote is nullptr!");
        return SOFTBUS_ERR;
    }

    MessageParcel data;
    if (!data.WriteCString(pkgName)) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN write client name failed!");
        return SOFTBUS_ERR;
    }
    if (!data.WriteUint32(addrTypeLen)) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN write addr type length failed!");
        return SOFTBUS_ERR;
    }
    if (!data.WriteRawData(addr, addrTypeLen)) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN write addr failed!");
        return SOFTBUS_ERR;
    }
    MessageParcel reply;
    MessageOption option;
    if (remote->SendRequest(SERVER_JOIN_LNN, data, reply, option) != 0) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN send request failed!");
        return SOFTBUS_ERR;
    }
    int32_t serverRet = 0;
    if (!reply.ReadInt32(serverRet)) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN read serverRet failed!");
        return SOFTBUS_ERR;
    }
    return serverRet;
}

 

再看对应 sub 中 SERVER_JOIN_LNN 值去调用下面这个方法:

int32_t SoftBusServerStub::JoinLNNInner(MessageParcel &data, MessageParcel &reply)
{
    const char *clientName = data.ReadCString();
    if (clientName == nullptr) {
        SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner read clientName failed!");
        return SOFTBUS_ERR;
    }
    uint32_t addrTypeLen;
    if (!data.ReadUint32(addrTypeLen)) {
        SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner read addr type length failed!");
        return SOFTBUS_ERR;
    }
    void *addr = (void *)data.ReadRawData(addrTypeLen);
    if (addr == nullptr) {
        SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner read addr failed!");
        return SOFTBUS_ERR;
    }
    int32_t retReply = JoinLNN(clientName, addr, addrTypeLen);
    if (!reply.WriteInt32(retReply)) {
        SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner write reply failed!");
        return SOFTBUS_ERR;
    }
    return SOFTBUS_OK;
}

 

可以看到显示读数据,然后调用 JoinLNN,你发现 stub 这个方法为空,但是要注意到这个方法是一个虚函数。

 

去查看它的子类 SoftBusServer:

int32_t SoftBusServer::JoinLNN(const char *pkgName, void *addr, uint32_t addrTypeLen)
{
    return LnnIpcServerJoin(pkgName, addr, addrTypeLen);
}

 

所以真正调用的是 LnnIpcServerJoin,我们看下他到底做了什么,这是真正的业务逻辑所在。

int32_t LnnIpcServerJoin(const char *pkgName, void *addr, uint32_t addrTypeLen)
{
    ConnectionAddr *connAddr = (ConnectionAddr *)addr;

    (void)addrTypeLen;
    if (pkgName == nullptr || connAddr == nullptr) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "parameters are nullptr!
");
        return SOFTBUS_INVALID_PARAM;
    }
    std::lock_guard<std::mutex> autoLock(g_lock);
    if (IsRepeatJoinLNNRequest(pkgName, connAddr)) {
        SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "repeat join lnn request from: %s", pkgName);
        return SOFTBUS_ALREADY_EXISTED;
    }
    int32_t ret = LnnServerJoin(connAddr);
    if (ret == SOFTBUS_OK) {
        ret = AddJoinLNNInfo(pkgName, connAddr);
    }
    return ret;
}

 

看一下,主要是有几个部分,第一查看参数有效性,第二是不是重复节点,使用连接地址创建连接,使用包名和地址建立映射。具体的感兴趣的小伙伴可以去查看一下。

 

 

 

原文标题:OpenHarmony软总线架构分析

文章出处:【微信公众号:HarmonyOS技术社区】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分