鸿蒙技术特性之分布式软总线

1,212 阅读10分钟

设备通信方式多种多样(USB/WIFI/BT等),不同通信方式使用差异很大且繁琐,同时通信链路的融合共享和冲突无法处理,通信安全问题也不好保证。分布式软总线是手机、平板、智能穿戴、智慧屏、车机等分布式设备的通信基座,为设备之间的互联互通提供了统一的分布式通信能力。 分布式软总线技术是基于华为多年的通信技术积累,参考计算机硬件总线,在1+8+N设备间搭建一条“无形”的总线,具备自发现、自组网、高带宽低时延的特点。

应用场景:

智能家居场景:在烹饪时,手机可以通过碰一碰和烤箱连接,并将自动按照菜谱设置烹调参数,控制烤箱来制作菜肴。与此类似,料理机、油烟机、空气净化器、空调、灯、窗帘等都可以在手机端显示并通过手机控制。设备之间即连即用,无需繁琐的配置。 多屏联动课堂:老师通过智慧屏授课,与学生开展互动,营造课堂氛围;学生通过手机完成课程学习和随堂问答。统一、全连接的逻辑网络确保了传输通道的高带宽、低时延、高可靠。

分布式软总线架构图:

鸿蒙分布式软总线的原理:

  1. 统一的通信协议:鸿蒙系统采用统一的通信协议,使不同设备之间可以进行无缝的通信。这个通信协议可以支持不同的通信方式,包括有线和无线通信。
  2. 分布式服务框架:鸿蒙系统提供了一个分布式服务框架,该框架可以将设备上的应用程序和服务进行管理和调度。通过这个框架,设备可以注册自己的服务,并可以通过分布式软总线来访问其他设备上的服务。
  3. 资源共享:鸿蒙系统通过分布式软总线实现了设备之间的资源共享。设备可以将自己的资源注册到分布式软总线上,其他设备可以通过分布式软总线来访问这些资源。例如,一个设备上的打印机可以注册到分布式软总线上,其他设备就可以通过分布式软总线来访问这个打印机,实现打印功能的共享。
  4. 事件通知机制:鸿蒙系统通过分布式软总线实现了设备之间的事件通知机制。当一个设备上发生了某个事件,比如温度传感器检测到了温度变化,它可以通过分布式软总线来通知其他设备。其他设备可以根据这个事件来做出相应的响应,比如调整空调的温度

设备间如何发现连接并传输数据:

软总线技术支持对不同协议的异构网络进行组网。传统场景下,需要蓝牙传输的两台设备必须都具有蓝牙,需要WiFi传输的设备必须都具有WiFi。而蓝牙/WiFi 之间是无法进行数据通信的。 软总线提出蓝牙/WiFi 融合网络组网技术(架构如下图所示),解决了不同协议设备进行数据通信的问题。使得多个鸿蒙设备能够自动构建一个逻辑全连接网络,用户或者业务开发者无需关心组网方式与物理协议。 传统开发模式: 在传统开发模式中开发者需要适配不同网络协议和标准规范。 分布式开发模式: 在HarmonyOS分布式开发模式中开发不再需要关心网络协议差异,业务开发与设备组网解耦,业务仅需监听设备上下线,开发成本大大降低。 软总线传输的优点: 高带宽(High Speed)、 低时延(Low Latency)、 高可靠(High Reliability) 极简协议 将中间的四层协议栈精简为一层提升有效载荷,有效传输带宽提升20%,极简协议在传统网络协议的基础上进行增强:

  • 流式传输:基于UDP实现数据的保序和可靠传输;
  • 双轮驱动:颠覆传统TCP每包确认机制;
  • 不惧网损:摒弃传统滑动窗口机制,丢包快速恢复,避免阻塞;
  • 不惧抖动:智能感知网络变化,自适应流量控制和拥塞控制;

软总线demo

1.dsoftbus_start()

入口启动函数,启动后循环发布、发现、发现节点后连接节点。

int dsoftbus_start(){
    if (init() < 0) {
        return -1;
    }

    if (!dsoftbus_init()) {
        return -2;
    }
    if (dsoftbus_publish() < 0) {
        return -3;
    }
    if (dsoftbus_create_session() < 0) {
        return -4;
    }
    if (dsoftbus_send() < 0) {
        return -5;
    }

    while (1) {
        osDelay(5000);
        dsoftbus_undiscover();
        osDelay(1000);
        dsoftbus_discovery();
        osDelay(1000);
        dsoftbus_node();
        dsoftbus_connect();
    }
    return 0;
}

2.init()初始化mutex及数据

int init(){
    if (pthread_mutex_init(&mutex_device, NULL) != 0)
        return -1;
    if (pthread_mutex_init(&mutex_session, NULL) != 0)
        return -2;
    pthread_mutex_lock(&mutex_session);
    for (int i = 0; i < MAX_DEVICE_NUMBER; i++) {
        allSessionInfos[i].session_id = -1;
        allSessionInfos[i].opened = false;
    }
    pthread_mutex_unlock(&mutex_session);
    return 0;
}

3.dsoftbus_init()初始化软总线

bool dsoftbus_init(){
    InitSoftBusServer();
    return GetServerIsInit();
}

4.dsoftbus_publish()软总线发布

int dsoftbus_publish(){
    if (isPublished) {
        return 0;
    }
    PublishInfo pub_info = {
        .publishId = publishId,
        .mode = DISCOVER_MODE_ACTIVE,
        .medium = COAP,
        .freq = LOW,
        .capability = g_capabilityMap[SHARE_CAPABILITY_BITMAP].capability,
        .capabilityData = NULL,
        .dataLen = 0,
    };

    IPublishCb pub_cb = {
        .OnPublishResult = OnPublishResult,
    };

    int32_t ret = PublishLNN(pkgName, &pub_info, &pub_cb);
    if (ret == SOFTBUS_OK) {
        isPublished = true;
        return 0;
    }
    printf("PublishService failed %ld\n", ret);
    return -1;
}

5.dsoftbus_discovery()软总线发现

 int dsoftbus_discovery(){
    if (isDiscoveryed) {
        return 0;
    }

    SubscribeInfo sub_info = {
        .subscribeId = publishId,
        .mode = DISCOVER_MODE_ACTIVE,
        .medium = COAP,
        .freq = LOW,
        .capability = g_capabilityMap[SHARE_CAPABILITY_BITMAP].capability,
        .capabilityData = NULL,
        .dataLen = 0,
        .isSameAccount = true,
        .isWakeRemote = true,
    };
    IRefreshCallback disc_cb = {
        .OnDeviceFound = OnDeviceFound,
        .OnDiscoverResult = OnDiscoverResult,
    };

    int32_t ret = RefreshLNN(pkgName, &sub_info, &disc_cb);
    printf("RefreshLNN ret %ld\n", ret);
    if (ret == SOFTBUS_OK) {
        isDiscoveryed = true;
        return 0;
    }
    return -1;
}

int dsoftbus_undiscover(){
    int32_t ret = StopRefreshLNN(pkgName, publishId);
    isDiscoveryed = false;
    printf("StopRefreshLNN ret %ld\n", ret);
    if (ret == SOFTBUS_OK) {
        return 0;
    }
    return -1;
}

6.dsoftbus_create_session()创建session

int dsoftbus_create_session(){
    INodeStateCb node_cb = {
        .events = 0xff,
        .onNodeOnline = OnNodeOnline,
        .onNodeOffline = OnNodeOffline,
        .onNodeBasicInfoChanged = OnNodeBasicInfoChanged,
        .onNodeStatusChanged = OnNodeStatusChanged,
    };

    ISessionListener session_cb = {
        .OnSessionOpened = OnSessionOpened,
        .OnSessionClosed = OnSessionClosed,
        .OnBytesReceived = OnBytesReceived,
    };

    int32_t ret = 0;
    ret = RegNodeDeviceStateCb(pkgName, &node_cb);
    printf("RegNodeDeviceStateCb ret %d \n", ret);
    if (ret < 0) {
        return -1;
    }

    if (GetLocalNodeDeviceInfo(pkgName, &local_node) != SOFTBUS_OK) {
        printf("GetLocalNodeDeviceInfo failed!\n");
        return -2;
    }

    ret = CreateSessionServer(pkgName, local_node.networkId, &session_cb);
    if (ret != SOFTBUS_OK) {
        return -3;
    }
    return 0;
}

7.dsoftbus_node()更新node信息

int dsoftbus_node(){
    NodeBasicInfo* nodeinfo = NULL;
    int32_t infoNum = 0;
    if (GetAllNodeDeviceInfo(pkgName, &nodeinfo, &infoNum) != SOFTBUS_OK){
        if (nodeinfo)
            FreeNodeInfo(nodeinfo);
        return -1;
    }
    printf("GetAllNodeDeviceInfo num %ld\n", infoNum);
    for (int j = 0; j < infoNum; j++) {

        pthread_mutex_lock(&mutex_session);
        for (int i = 0; i < MAX_DEVICE_NUMBER; i++) {
        if (strcmp(allSessionInfos[i].node.networkId, nodeinfo[j].networkId) == 0) {
            printf("same session %s\n", allSessionInfos[i].node.networkId);
            break;
            }
        if (allSessionInfos[i].session_id == -1) {
            printf("add session[%d] %s\n", i, nodeinfo[j].networkId);
            allSessionInfos[i].session_id = 0;
            memcpy(&allSessionInfos[i].node, &nodeinfo[j], sizeof(NodeBasicInfo));
            break;
            }
        }
        pthread_mutex_unlock(&mutex_session);
    }

    FreeNodeInfo(nodeinfo);
    return 0;
}

8.dsoftbus_connect()连接session

void dsoftbus_connect(){
    static SessionAttribute session_attr = {
        .dataType = TYPE_BYTES,
        .linkTypeNum = 3,
        .linkType = { LINK_TYPE_WIFI_WLAN_5G, LINK_TYPE_WIFI_WLAN_2G, LINK_TYPE_WIFI_P2P },
    };

    pthread_mutex_lock(&mutex_session);
    for (int i = 0; i < MAX_DEVICE_NUMBER; i++) {
        if (allSessionInfos[i].session_id == 0 && allSessionInfos[i].node.networkId[0] != 0) {
            int32_t id = OpenSession(local_node.networkId, allSessionInfos[i].node.networkId, allSessionInfos[i].node.networkId, groupId, &session_attr);
            printf("OpenSession [%d] %s id = %d\n", i, allSessionInfos[i].node.networkId, id);
            if (id > 0) {
                allSessionInfos[i].session_id = id;
                allSessionInfos[i].opened = false;
            }
        }
    }
    pthread_mutex_unlock(&mutex_session);
}

9.dsoftbus_send()发送数据

void dsoftbus_send_thread(void *arg){
    while(1)
    {
        osDelay(5000);
        static int count = 0;
        char buffer[30];
        sprintf(buffer, "hello dsoftbus %d", count++);
        pthread_mutex_lock(&mutex_session);
        for (int i = 0; i < MAX_DEVICE_NUMBER; i++) {
            if (allSessionInfos[i].session_id > 0 && allSessionInfos[i].opened) {
                int32_t ret = SendBytes(allSessionInfos[i].session_id, buffer, strlen(buffer) + 1);
                printf("SendBytes sessionid %d ret %d str: %s\n", allSessionInfos[i].session_id, ret, buffer);
            }
        }
        pthread_mutex_unlock(&mutex_session);
    }
}

int dsoftbus_send()
{
    osThreadAttr_t attr;
    attr.name = "send task";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 65536;
    attr.priority = 24;

    if (osThreadNew(bus_send_thread, NULL, &attr) == NULL) {
        printf("Falied to create bus_start!\n");
        return -1;
    }
    return 0;
}

10.回调 接收数据回调

static void OnBytesReceived(int sessionId, const void* data, unsigned int len){
    printf("<OnBytesReceived>CB: data=%s datalen=%u sessionId=%d\n", (char *)data, len, sessionId);
}

鸿蒙是如何让1套OS适配所有终端的?

这就要说到鸿蒙是一款 微内核操作系统

什么是微内核?

微内核的概念是由Richard Rashid在卡内基梅隆(Carnegie-Mellon)大学开发Mach操作系统时提出的。 微内核从原理上了来说很简单,就是把最不能去除的操作系统功能留在内核层,其他的东西统统送到外核甚至应用层去。

那么微内核是如何工作的呢?

微内核中定义了一种进程间通信的机制------消息。当应用程序请求相关服务时,会向微内核发送一条与此服务对应的消息,微内核再把这条消息发送给相关的服务进程,接着服务进程会完成相关的服务。 我们举一个应用程序读取一个文件的例子。首先,应用程序调用文件系统API,比如open()函数,请求打开一个文件。操作系统内核识别这是一个文件系统相关的请求,便将其转发给负责文件系统的服务进程。文件系统服务进程接收到打开文件的请求,根据文件路径在磁盘上查找对应的文件数据块,并将文件的元数据信息返回给内核。内核将文件元数据信息返回给应用程序,比如文件描述符、文件大小等。应用程序再次调用read()函数请求读取文件内容。内核将读取文件内容的请求转发给文件系统服务进程。文件系统服务进程根据文件描述符定位到文件数据块,将数据读取到内存缓冲区,并通过IPC机制将数据返回给内核。内核将读取到的文件数据返回给应用程序。

微内核的应用及优缺点

优点: 高可靠性和可扩展性:关键内核功能最小化,减少内核代码的复杂度和错误概率。各服务进程相互隔离,一个服务进程的故障不会影响整个系统。可以根据需求有选择地集成所需的服务模块,提高系统的可定制性。 高安全性: 内核与服务进程间的特权级隔离,提高了系统的安全性。可以有针对性地增加可信服务模块,满足关键系统的安全需求。 高实时性: 关键实时任务可直接在内核中运行,不受其他服务模块的影响。内核功能最小化,上下文切换开销较小。 良好的可移植性: 微内核结构清晰,便于移植到不同硬件平台。服务模块相对独立,便于跨平台移植。

缺点: 性能损耗:服务进程间的通信会带来一定的性能开销。部分功能从内核空间移到用户空间会影响系统性能。 设计复杂度:微内核设计需要解决进程间通信、资源管理等复杂问题。需要仔细权衡哪些功能放在内核中,哪些放在用户空间。

说到微内核,就不得不提到宏内核

宏内核是一种传统的内核结构,它将进程管理,内存管理等各项服务功能都放到内核中去。 Linux、安卓都是比较典型的宏内核结构。当今主流商用系统Windows、MacOSX都属于混合内核。 微内核架构模式实现了模块化解耦,不同设备的弹性部署。

结语

鸿蒙系统分布式架构能让你在使用某个APP软件的时候,比如看视频,可以把屏幕随意切换到电视、电脑、手机、平板、投影仪等任何一个设备的界面上。可以在手表手机等任意一台设备上操作所有的设备,岂不是很爽,对于比较懒的同学是很大的福音,例如作者本人

作者:洞窝-王有为

“本文正在参加华为鸿蒙有奖征文征文活动”