Harmony软总线技术浅析

W_T_G_ 2022-01-18 23:34:37
加精

1 软总线技术原理概述

1.1 什么是总线

在计算机系统中,各个部件之间传送信息的公共通路叫总线,微型计算机是以总线结构来连接各个功能部件的。按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制 信号。

传统总线的典型特征:即插即用、高带宽、低时延、高可靠、标准

 

1. 2  什么是分布式软总线?

软总线与总线协议类似存在相似的功能但又有差异。总线协议中多个设备通过公共通信干线来进行通信,设备需要具备收发数据功能。openharmony 软总线需要将不同设备整合到一起,由于不同的设备通信存在差异,如wifi与蓝牙之间通信存在差异,软总线(bus)需要具备有处理不同类型设备之间通信功能。分布式软总线其实指的是一种多类型网络通信协议,它可以把所有鸿蒙设备连在一起,通过该协议进行通信。这里华为把它抽象成一根类似物理上的总线,就好像I2C总线,在SDA、SCL两根线上可以挂载许多设备,设备可以通过I2C总线进行互相通信。

分布式软总线技术是基于华为多年的通信技术积累,参考计算机硬件总线,在1+8+N设备间搭建一条“无形”的总线,具备自动发现/即连即用、高带宽、低时延、高可靠、开放/标准的特点(1+8+N:1指的是手机8代表车机、音箱、耳机、手表/手环、平板、大屏、PC、AR/VRN泛指其他IOT设备)

 

1.3 分布式软总线功能和原理

1.3.1 分布式软总线的架构

通过协议货架和软硬协同层屏蔽各种设备的协议差别,总线中枢模块负责解析命令完成设备间发现和连接,通过任务和数据两条总线实现设备间文件传输、消息传输等功能。

分布式总线的总体目标是实现设备间无感发现,零等待传输。实现这个目标需要解决三个问题:

设备间发现与连接

传统的设备发现是手动的,需要人干预.比如手机上有很多照片需要传到个人PC上,我们可以采用蓝牙传输,首先要打开手机和PC的蓝牙发现功能,手机或者PC点击搜索设备,然后互相配对授权即可连接上,然后能够发送照片。

软总线提出了自动发现的概念,实现用户零等待的自发现体验,附近同账号的设备自动发现无需等待。

 

多设备组网

上面的例子中手机传照片是通过蓝牙,假如PC没有蓝牙功能只有WIFI,在传统的场景中这种可能就不能实现分享传输了。

然而软总线做到了让手机通过蓝牙传输,PC通过WIFI来接收照片。软总线提出了异构网络组网可以很好解决设备间不同协议如何交互的问题。

设备上线后会向网络层注册,同时网络层会与设备建立通道连接,实时检测设备的变换。网络层负责管理设备的上线下线变换,设备间可以监听自己感兴趣的设备,设备上线后可以立即与其建立连接,实现零等待体验。

软总线可以自动构建一个逻辑全连接网络,用户或者业务开发者无需关心组网方式与物理协议。对于软件开发者来说软总线异构组网可以大大降低其开发成本。

 

3  多设备多协议间数据传输

传统协议的传输速率差异非常大,时延也难以得到保证。

软总线传输要实现的目标:

高带宽(High Speed)

低时延(Low Latency)

高可靠(High Reliability)

软总线通过极简协议来实现的这三大目标。将中间的四层协议栈精简为一层提升有效载荷,有效传输带宽提升20%.

极简协议在传统网络协议的基础上进行增强:

流式传输:基于UDP实现数据的保序和可靠传输;

双轮驱动:颠覆传统TCP每包确认机制;

不惧网损:摒弃传统滑动窗口机制,丢包快速恢复,避免阻塞;

不惧抖动:智能感知网络变化,自适应流量控制和拥塞控制;

 

2 软总线源代码的构建和测评

2.1 构建环境:

2.1.1安装nodejs, npm, hpm-cli命令行工具

sudo apt install nodejs
sudo apt install npm
npm install -g @ohos/hpm-cli

查看安装结果:

 

2.1.2 下载HarmonyOS代码

通过鸿蒙在码云的代码仓库获取最新版本代码:

https://gitee.com/openharmony/communication_dsoftbus

将项目解压后得到以下文件:

 

2.1.3 将linux shell改为bash

ls -l $(which sh)
# 如果指向的不是bash,则按以下方式修改:

# 方法一:执行以下命令,然后选择no
dpkg-reconfigure dash

# 方法二:先删除sh,再重新创建软连接
rm -f /bin/sh
ln -s bash /bin/sh

这里我使用方法一进行设置:

系统会出现下图弹窗,选择No

 

2.1.4 搭建python环境

HarmonyOS中引入了很多python的依赖函数,需要搭建python环境,并且确保版本为3.7以上。

参考这篇帖子配置python环境:Linux搭建python环境详解_Linux_脚本之家 (jb51.net)

使用python -v命令查看环境是否配置成功

 

2.2 源码编译

根据参考资料中的软总线模块依赖文件 BUILD.gn,建立makefile文件,并将makefile文件创建在harmonyOS目录下

ROOT_DIR:= ~/communication_dsoftbus-master

INC_DIR:=  \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/include    \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/os_adapter/include    \
$(ROOT_DIR)/foundation/communication/interfaces/kits/softbus_lite/discovery     \
$(ROOT_DIR)/third_party/cJSON   \
$(ROOT_DIR)/third_party/bounds_checking_function/include    \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/discovery_service/include     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/authmanager/include     \
$(ROOT_DIR)/base/startup/interfaces/kits/syspara_lite   \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/include/libdistbus     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/include/utils  \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/libdistbus     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/utils \
$(ROOT_DIR)/kernel/liteos_a/lib/libsec/include   \
$(ROOT_DIR)/foundation/communication/interfaces/kits/softbus_lite/transport     \
$(ROOT_DIR)/base/security/interfaces/innerkits/hichainsdk_lite     \
$(ROOT_DIR)/third_party/mbedtls/include   \
$(ROOT_DIR)/base/security/frameworks/hichainsdk_lite/source/huks_adapter     \
$(ROOT_DIR)/base/security/interfaces/kits/iam_lite   \
$(ROOT_DIR)/foundation/communication/interfaces/kits/softbus_lite/discovery/    \


SRCS:= \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/authmanager/source/auth_conn.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/authmanager/source/auth_interface.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/authmanager/source/bus_manager.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/authmanager/source/msg_get_deviceid.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/authmanager/source/wifi_auth_manager.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/source/coap_adapter.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/source/coap_discover.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/source/coap_socket.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/source/json_payload.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/source/nstackx_common.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/source/nstackx_device.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/discovery_service/source/coap_service.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/discovery_service/source/common_info_manager.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/discovery_service/source/discovery_service.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/os_adapter/source/L1/os_adapter.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/libdistbus/auth_conn_manager.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/libdistbus/tcp_session.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/libdistbus/tcp_session_manager.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/libdistbus/trans_lock.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/utils/aes_gcm.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/utils/message.c     \
$(ROOT_DIR)/foundation/communication/services/softbus_lite/trans_service/source/utils/tcp_socket.c     \


OBJS:= $(patsubst %.c, %.o, $(SRCS))

LIBS:=

CC:=gcc
CXXFLAGS:= -fPIC -Wall $(addprefix -I , $(INC_DIR)) $(LIBS) -Wno-deprecated

all:
    $(CC) -c $(SRCS) $(CXXFLAGS)
    rm -rf ./obj
    mv *.o ./obj/
    $(CC) -shared -o ./obj/libsoftbus_lite.so ./obj/*.o


clean:
    rm -rf ./obj

使用make命令,执行makefile文件来编译源码。

 

 

2.3 编写测试程序

根据鸿蒙开发者文档,创建测试demo

#include<discovery_service.h>
#include<session.h>
#include<coap_discover.h>
#include<tcp_session_manager.h>
#include<nstackx.h>


#include<stdio.h>
#include<string.h>



// 定义业务⾃身的业务名称,会话名称及相关回调

const char *g_pkgName = "BUSINESS_NAME";
const char *g_sessionName = "SESSION_NAME";
struct ISessionListener * g_sessionCallback= NULL;


#define NAME_LENGTH 64
#define TRANS_FAILED -1


// 回调实现:接收对方通过SendBytes发送的数据,此示例实现是接收到对端发送的数据后回复固定消息

void OnBytesReceivedTest(int sessionId, const void* data, unsigned int dataLen)
{
    printf("OnBytesReceivedTest\n");
    printf("Recv Data: %s\n", (char *)data);
    printf("Recv Data dataLen: %d\n", dataLen);
    char *testSendData = "Hello World, Hello!";
    SendBytes(sessionId, testSendData, strlen(testSendData));
    return;
}


// 回调实现:用于处理会话关闭后的相关业务操作,如释放当前会话相关的业务资源,会话无需业务主动释放

void OnSessionClosedEventTest(int sessionId)
{
    printf("Close session successfully, sessionId=%d\n", sessionId);
}



// 回调实现:用于处理会话打开后的相关业务操作。返回值为0,表示接收;反之,非0表示拒绝。此示例表示只接受其他设备的同名会话连接

int OnSessionOpenedEventTest(int sessionId)
{
    char sessionNameBuffer[NAME_LENGTH+1];

    if(GetPeerSessionName(sessionId,sessionNameBuffer,NAME_LENGTH) == TRANS_FAILED) {
        printf("GetPeerSessionName faild, which sessionId = %d\n",sessionId);
        return -1;
    }

    if (strcmp(sessionNameBuffer, g_sessionName) != 0) {
        printf("Reject the session which name is different from mine, sessionId=%d\n", sessionId);
        return -1;
    }

    printf("Open session successfully, sessionId=%d\n", sessionId);
    return 0;
}



// 向SoftBus注册业务会话服务及其回调

int StartSessionServer()
{
    if (g_sessionCallback == NULL) {
        g_sessionCallback = (struct ISessionListener*)malloc(sizeof(struct ISessionListener));
    }

    if (g_sessionCallback == NULL) {
        printf("Failed to malloc g_sessionCallback!\n");
        return -1;
    }

    g_sessionCallback->onBytesReceived = OnBytesReceivedTest;
    g_sessionCallback->onSessionOpened = OnSessionOpenedEventTest;
    g_sessionCallback->onSessionClosed = OnSessionClosedEventTest;
    int ret = CreateSessionServer(g_pkgName, g_sessionName, g_sessionCallback);

    if (ret < 0) {
        printf("Failed to create session server!\n");
        free(g_sessionCallback);
        g_sessionCallback = NULL;
    }
    return ret;
}



// 从SoftBus中删除业务会话服务及其回调

void StopSessionServer()
{
    int ret = RemoveSessionServer(g_pkgName, g_sessionName);

    if (ret < 0) {
        printf("Failed to remove session server!\n");
        return;
    }

    if (g_sessionCallback != NULL) {
        free(g_sessionCallback);
        g_sessionCallback = NULL;
    }
}


// 回调函数声明:

void onSuccess(int publishId)
{
    printf("publish succeeded, publishId = %d\r\n", publishId);
    char ipbuff[NSTACKX_MAX_IP_STRING_LEN] = {"0.0.0.0"};
    CoapGetIp(ipbuff,NSTACKX_MAX_IP_STRING_LEN,0);
    printf("CoapGetIp = %s\n",ipbuff);
    if(StartSessionServer()!=-1)
        printf("StartSessionServer successed!\n");
}

void onFail(int publishId, PublishFailReason reason)
{
    printf("publish failed, publishId = %d, reason = %d\r\n", publishId, reason);
}


int main(){
    // 服务发布接口使用
    PublishInfo info = {0};
    IPublishCallback cb = {0};
    cb.onPublishSuccess = onSuccess;
    cb.onPublishFail = onFail;
    char a[] = "456";
    info.capabilityData = a;
    info.capability = "ddmpCapability";
    info.dataLen = strlen(a);
    info.medium = 2;
    info.publishId = 1;
    PublishService("cxx", &info, &cb);

}

将前面编译生成的链接文件放入系统链接库中。
动态编译测试文件,生成可执行文件执行:

gcc testDemo.c -o testDemo -g -lsoftbus_lite -lrt –lpthread

执行结果如下图:

 

3 软总线源代码分析

分布式软总线组件主要代码目录结构如下:

 

将软总线代码结构主体为四个部分:adapter,authentication,discovery,transmission。

其中,adapter属于适配层,用于适配不同OS;authentication负责提供设备认证机制和设备管理功能;discovery是基于coap协议的设备发现;transmission主要完成数据传输。

 

3.3 transmission

transmission模块依赖于系统OS提供的网络socket服务,向认证模块提供认证通道管理和认证数据的收发;向业务模块提供session管理和基于session的数据收发功能,并且通过GCM模块的加密功能提供收发报文的加解密保护。

authmanager模块中存在StartBus()函数,其中,StartListener()函数负责为认证模块提供通道完成初始化,StartSession()函数负责初始化业务的session管理:

int StartBus(void)
{
    if (g_busStartFlag == 1) {
        return 0;
    }
    DeviceInfo *info = GetCommonDeviceInfo();

    if (info == NULL) {
        return ERROR_FAIL;
    }

    g_baseLister.onConnectEvent = OnConnectEvent;
    g_baseLister.onDataEvent = OnDataEvent;
    int authPort = StartListener(&g_baseLister, info->deviceIp);

    if (authPort < 0) {
        SOFTBUS_PRINT("[AUTH] StartBus StartListener fail\n");
        return ERROR_FAIL;
    }

    info->devicePort = authPort;
    int sessionPort = StartSession(info->deviceIp);

    if (sessionPort < 0) {
        SOFTBUS_PRINT("[AUTH] StartBus StartSession fail\n");
        StopListener();
        return ERROR_FAIL;
    }

    AuthMngInit(authPort, sessionPort);
    g_busStartFlag = 1;


    SOFTBUS_PRINT("[AUTH] StartBus ok\n");
    return 0;

}

 

3.1 authentication

设备之间互联是基于系统的IoT设备(如AI音箱、智能家居、智能穿戴等设备)与IoT主控设备(手机、平板等)间建立点对点的信任关系,并在具备信任关系的设备间,搭建安全的连接通道,实现用户数据端到端加密传输。

IoT主控设备和IoT设备建立点对点信任关系的过程,实际上是相互交换IoT设备的身份标识的过程。

authentication是openharmony为设备提供认证机制的模块,模块内的处理流程为

https://oscimg.oschina.net/oscnet/up-eefc5e1201b40a126b24fd411e72ec3e6fc.png

为了实现用户数据在设备互联场景下在各个设备之间的安全流转,需要保证设备之间相互正确可信,即设备和设备之间建立信任关系,并能够在验证信任关系后,搭建安全的连接通道,实现用户数据的安全传输。设备之间的信任关系在本文档中涉及IoT主控设备和IoT设备之间建立的可信关系。

IoT设备互联安全
设备互联支持基于HarmonyOS的IoT设备(如AI音箱、智能家居、智能穿戴等设备)与IoT主控设备间建立点对点的信任关系,并在具备信任关系的设备间,搭建安全的连接通道,实现用户数据端到端加密传输。

IoT主控设备的IoT业务身份标识
IoT主控设备为不同的IoT设备管理业务生成不同的身份标识,形成不同IoT管理业务间的隔离,该标识用于IoT主控设备与IoT设备之间的认证以及通信。IoT业务身份标识为椭圆曲线公私钥对(Ed25519公私钥对)。

IoT设备身份标识
IoT设备会生成各自的设备身份标识,用来与IoT主控设备通信。该身份标识同样为椭圆曲线公私钥对(Ed25519公私钥对);IoT设备私钥不出IoT设备,设备每次恢复出厂设置,会重置这个公私钥对。
上述身份标识可用于IoT主控设备与IoT设备间的安全通信:当IoT主控设备与IoT设备通过信任绑定流程交换业务身份标识或设备标识后,可以进行密钥协商并建立安全通信通道。

设备间点对点的信任绑定
IoT主控设备和IoT设备建立点对点信任关系的过程,实际上是相互交换IoT设备的身份标识的过程。
在点对点建立信任关系的过程中,用户需要在IoT主控设备上,输入IoT设备上提供的PIN码:对于有屏幕的设备,该PIN码动态生成;对于没有屏幕的设备,该PIN码由设备生产厂家预置;PIN码的展示形式,可以是一个用户可读的数字,也可以是一个二维码。随后,IoT主控设备和IoT设备间使用PAKE协议完成认证和会话密钥协商过程,并在此基础上,通过协商出的会话密钥加密传输通道用于交换双方设备的身份标识公钥。

IoT主控设备与IoT设备间的通信安全
当建立过信任关系的IoT主控设备与IoT设备间进行通信时,双方在完成上述信任关系绑定后,基于本地存储的对端身份公钥相互进行认证;在每次通信时基于STS协议完成双向身份认证以及会话密钥协商,之后设备使用此会话密钥加密双方设备间的传输通道。

流程详解与关键代码数据结构:
初始化代码流程如下图:

https://oscimg.oschina.net/oscnet/up-7e713b25f1fc2ae56e25640ca3127ed29cb.png

该部分主要由discover 模块调用BusManager 函数开始,若flag = 1,则进行StartBus 函数的执行,它的实现主要是调用了StartListener 函数及StartSession 函数,实现对设备进行认证及加解密的过程。

StartSession 函数如下:

int StartSession(const char *ip)
{
    int port = CreateTcpSessionMgr(true, ip);
    return port;
}


int CreateTcpSessionMgr(bool asServer, const char* localIp)
{
    if (g_sessionMgr != NULL || localIp == NULL) {
        return TRANS_FAILED;
    }

    g_sessionMgr = malloc(sizeof(TcpSessionMgr));

    if (g_sessionMgr == NULL) {
        return TRANS_FAILED;
    }

    (void)memset_s(g_sessionMgr, sizeof(TcpSessionMgr), 0, sizeof(TcpSessionMgr));
    g_sessionMgr->asServer = asServer;
    g_sessionMgr->listenFd = -1;
    g_sessionMgr->isSelectLoopRunning = false;

    if (InitTcpMgrLock() != 0 || GetTcpMgrLock() != 0) {
        FreeSessionMgr();
        return TRANS_FAILED;
    }

    for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
        g_sessionMgr->sessionMap_[i] = NULL;
    }

    for (int i = 0; i < MAX_SESSION_SERVER_NUM; i++) {
        g_sessionMgr->serverListenerMap[i] = NULL;
    }

    if (ReleaseTcpMgrLock() != 0) {
        FreeSessionMgr();
        return TRANS_FAILED;
    }

    int listenFd = OpenTcpServer(localIp, DEFAULT_TRANS_PORT);

    if (listenFd < 0) {
        SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr OpenTcpServer fail\n");
        FreeSessionMgr();
        return TRANS_FAILED;
    }

    int rc = listen(listenFd, LISTEN_BACKLOG);

    if (rc != 0) {
        SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr listen fail\n");
        CloseSession(listenFd);
        FreeSessionMgr();
        return TRANS_FAILED;
    }

    g_sessionMgr->listenFd = listenFd;

    signal(SIGPIPE, SIG_IGN);

    if (StartSelectLoop(g_sessionMgr) != 0) {
        SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr StartSelectLoop fail\n");
        CloseSession(listenFd);
        FreeSessionMgr();
        return TRANS_FAILED;
    }

    return GetSockPort(listenFd);

}

StartSession 该函数只有一个参数,即const char *ip,也就是一个IP,和StartListener函数中的IP 是一样的。该函数是为全局变量g_sessionMgr 申请空间及初始化,然后根据所给的参数创建socket 文件描述符并监听,之后通过调用StartSelectLoop 函数创建SelectSessionLoop 的线程,该线程将socket 文件描述符加入集合,并调用select 函数进行监控,若函数的返回值大于0,则调用ProcessData 函数,该函数有两个分支,若socket 未创建session则为其创建session;若已创建session,则处理其数据部分。

当初步建立信任关系的IoT主控设备与IoT设备间在进行通信时,双方首先完成信任关系绑定,然后基于存储在本地的对端身份公钥相互进行认证;在每次通信时完成双向身份认证以及会话密钥协商,之后设备使用此会话密钥来解密双方设备间的传输通道。

 

3.2 discovery

用户使用发现功能时,需要保证发现端设备与被发现端设备在同一个局域网内,并且互相能收到对方以下流程的报文。

(1)发现端设备,发起discover请求后,使用coap协议在局域网内发送广播。
(2)被发现端设备使用PublishService接口发布服务,接收端收到广播后,发送coap协议单播给发现端。
(3)发现端设备收到报文会更新设备信息。

下面是设备的定义,分为本地设备(被发现端设备)和外部设备,从中不难看出鸿蒙OS的分布式特性,本地设备可以通过PublishService函数将自身的服务发布出去,供外部设备发现并使用。鸿蒙OS的设备发现机制是被发现设备在COAP端口监听来自发现设备的广播包,这也符合按需使用的原则。

typedef enum {
    ONLINE = 0,
    OFFLINE,
} NetworkState;

typedef struct DeviceInfo {
    char deviceName[MAX_DEV_NAME_LEN];
    char deviceId[MAX_DEV_ID_LEN];
    char deviceIp[MAX_DEV_IP_LEN];
    char version[MAX_DEV_VERSION_LEN];
    char softwareVersion[MAX_SOFTWARE_VERSION_LEN];
    char networkName[MAX_DEV_NETWORK_LEN];

    int deviceType;
    int devicePort;
    NetworkState networkState;
    int isAccountTrusted;
} DeviceInfo;

 

设备发现整体流程总结:

1 设备发现部分代码(主要是轻量设备侧)主入口函数为PublishService()函数。

2 PublishService()函数会检查软总线的服务是否已经初始化过,如果没有则会初始化软总线的所有服务,包括:a、基于UDP的COAP协议discover发现服务。b、wifi设备状态监听服务。c、基于TCP的认证服务。d、基于TCP的session会话管理服务。

3 PublishService()然后会把模块的信息加入g_publishModule全局数组中。

4 回调模块的发布成功回调函数。

 

3.4 adapter

adapter属于适配层,用于适配不同OS。此模块与其他几个模块共同完成适配功能。

下图为adapter目录结构:

 

 

作者:NP435

参考资料:

深度解读设备的“万能语言”鸿蒙系统的分布式软总线能力_华为云官方博客-CSDN博客_鸿蒙系统分布式怎么理解

docs: OpenHarmony documentation | OpenHarmony开发者文档 - Gitee.com

鸿蒙HarmonyOS技术社区-鸿蒙官方合作伙伴-51CTO.COM

润小云的个人空间 - OSCHINA - 中文开源技术交流社区

Linux搭建python环境详解_Linux_脚本之家 (jb51.net)

...全文
17377 2 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
ljp9405312 2022-12-05
  • 打赏
  • 举报
回复

ROOT_DIR:= ~/communication_dsoftbus-master

INC_DIR:=
$(ROOT_DIR)/foundation/communication/services/softbus_lite/discovery/coap/include
$(ROOT_DIR)/foundation/communication/services/softbus_lite/os_adapter/include \

ROOT_DIR:= ~/communication_dsoftbus-master
这个目录作为 根目录 ???
另外,我的整版本 $(ROOT_DIR)/foundation/communication/services/
没有services 目录??

Edice 2022-01-25
  • 打赏
  • 举报
回复

软总线这个概念解释的好清楚,我一个小白竟然也懂了

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧