鸿蒙通信softbus_lite组件分析

文章目录

    • 概述
    • 运行机理
      • 内部结构
      • PublishService函数
      • coap服务器
      • 设备认证服务器
      • 会话管理服务器
    • 对外接口
      • 设备发现接口
      • 数据传输接口
    • 使用示例
      • 标准使用方法
      • 测试方法

概述

softbus_lite组件是在受限设备上提供数据传输服务,除此之外,还提供设备发现,设备认证,通信安全功能。

运行机理

内部结构

softbus_lite由coap服务器,消息队列处理线程,设备认证服务器,会话管理服务器四部分组成。其中coap服务器是UDP服务器,端口为5684,用于设备发现。消息队列用于内部消息传递,目前仅用在创建设备认证服务器和会话管理服务器上,不对外提供服务。设备认证服务器是TCP服务器,其端口不固定,用于设备认证,密钥协商。会话管理服务器是TCP服务器,其端口不固定,用于数据传输。
softbus_lite内部结构图

softbus_lite
Coap Server
消息队列处理线程
认证服务器
会话服务器
TCP
数据传输
TCP
设备认证
消息队列处理
UDP:5684
设备发现

softbus_lite支持网口和Wifi两种通信方式。

PublishService函数

PublishService函数的功能是创建coap服务器,消息队列处理线程,设备认证服务器,会话管理服务器,声明一个功能模块。只有调用了PublishService函数,softbus_lite才能向外提供服务。
PublishService函数处理流程如下。

PublishService
InitService
CoapInitDiscovery
CoapWifiEventThread
DoRegistService
写CoapHandleWifiEvent/1
DoRegistService
AddPublishModule
InitService
CoapRegistService
GetCapablityAndData
消息队列
handle.handler
ReadMsgQue
CreateCoapListenThread
CreateMsgQueThread
CoapInitWifiEvent
CoapInitSocket
CoapWriteMsgQueue:UPDATE_IP_EVENT
CoapInitDiscovery
CoapInit
RegisterWifiCallback:WifiEventTrigger
InitCommonManager

由于CoapInitDiscovery函数已打开消息队列处理线程,而CoapWriteMsgQueue(UPDATE_IP_EVENT)函数向消息队列发送了消息,将触发消息队列处理流程,将调用g_wifiCallback(para),这时g_wifiCallback为WifiEventTrigger,para为1,所以处理流程如下。

WifiEventTrigger
DoRegistService:COAP
CoapRegisterDeviceInfo
BusManager:1
CoapGetIp
GetCommonDeviceInfo

主要函数说明:

函数名 功能说明
InitCommonManager() 为g_deviceInfo赋值,包括deviceId,deviceName,version
RegisterWifiCallback(WifiEventTrigger) 将g_wifiCallback赋值为WifiEventTrigger
CoapInitSocket() 在端口5684上创建coap服务器,设置其socket为g_serverFd,设置g_msgId为0
CoapInitWifiEvent() 创建消息队列“/wifiQue” ,设置其ID为g_wifiQueueId
CreateMsgQueThread() 创建消息队列处理线程
CreateCoapListenThread() 创建coap服务器处理线程
CoapWriteMsgQueue(UPDATE_IP_EVENT) 向消息队列发送消息(消息结构为AddressEventHandler,handler为CoapHandleWifiEvent,state为UPDATE_IP_EVENT),触发消息队列处理
CoapWifiEventThread() 消息队列处理线程,不停的从消息队列中取消息,执行消息的处理函数
WifiEventTrigger(para) para为1时打开认证服务器和会话管理服务器,否则关闭认证服务器和会话管理服务器,同时更新相关信息
GetCommonDeviceInfo() 返回g_deviceInfo
CoapGetIp(wifiIp, MAX_DEV_IP_LEN, 0) 获取本地IP,先读网口,网口不存在则读wifi地址
BusManager(1) 为g_baseLister的onConnectEvent和onDataEvent赋值;启动认证服务器及处理线程;启动会话服务器及处理线程;保存认证端口为g_authPort,会话端口为g_sessionPort;
CoapRegisterDeviceInfo() 从g_deviceInfo中获取deviceIp,deviceName,deviceId,deviceType,更新g_localDeviceInfo,g_interfaceList,g_networkType
DoRegistService(COAP) 获取capabilityBitmap,更新g_capabilityData,g_localDeviceInfo.capabilityBitmap,g_localDeviceInfo.serviceData(认证端口)
AddPublishModule(moduleName, info) 保存模块信息,包括名称,能力数据,publishId
GetCapablityAndData(capabilityBitmap, g_capabilityData, MAX_SERVICE_DATA_LEN) 获取所有模块的能力bitmap和能力数据
CoapRegistService(capabilityBitmap, MAX_CAPABILITY_NUM, g_capabilityData) 将能力bitmap和能力数据保存到g_localDeviceInfo中

coap服务器

coap服务器用于响应设备发现,检查接收报文是否合法,向外部设备提供自身的设备名称,设备ID,版本,模式,认证端口,IP地址,能力值等信息。其处理流程如下。

CoapReadHandle
PostServiceDiscover
COAP_SoftBusDecode
CoapSocketRecv
CoapResponseService
GetServiceDiscoverInfo

主要函数说明:

函数名 功能说明
CoapReadHandle() coap服务器处理线程,不停监听coap服务器端口,收到报文后向对端发送自身设备信息
CoapSocketRecv(socketFd, recvBuffer, COAP_MAX_PDU_SIZE) 从coap服务器端口获取报文
COAP_SoftBusDecode(&decodePacket, recvBuffer, nRead) 从接收报文中解析信息,填充到COAP_Packet结构中
PostServiceDiscover(&decodePacket) 解析信息,组织发送报文并发送
GetServiceDiscoverInfo(pkt->payload.buffer, pkt->payload.len, &deviceInfo, &remoteUrl) 从接收报文中解析信息,填充deviceInfo和remoteUrl
CoapResponseService(pkt, remoteUrl, wifiIpAddr) 从g_localDeviceInfo中获取设备名称,设备ID,版本,模式,认证端口,IP地址,能力值等,发送给对端5684端口

设备认证服务器

设备认证服务器用于设备认证,建立认证会话密钥,向外提供会话端口。其处理流程如下。

WaitProcess
ProcessConnectEvent
ProcessDataEvent
ProcessAuthData
AuthInterfaceOnDataReceived
OnDataReceived
ParsePacketHead
AuthConnRecv
OnModuleMessageReceived
DecryptMessage
AddAuthConnToList
创建AuthConn结构并赋值fd/ip

由于AuthInterfaceOnDataReceived函数主要处理流程在base/security/deviceauth目录下,这里不再展开。
OnModuleMessageReceived函数处理流程如下:

OnModuleMessageReceived
OnVerifyIp
获取CODE值
AuthConnPostMessage
MsgVerifyIpPack
MsgVerifyIpUnPack
BusGetLocalDeviceInfo

可见,如果客户端发送报文体中CODE域值为CODE_VERIFY_IP(即为1)时,服务器会将自身的会话端口返回给客户端。

从上可以看出,认证服务器主要工作有两个:

  1. 认证连接维护:接收客户端认证连接,记录到g_fdMap;
  2. 响应认证数据:解析客户端的认证数据,解析,并作相应处理;

主要函数说明:

函数名 功能说明
WaitProcess() 设备认证服务器处理线程
ProcessAuthData(g_listenFd, &readSet) 接收客户端设备认证连接,接收客户端认证数据,进行认证处理
ProcessConnectEvent(g_dataFd, inet_ntoa(addrClient.sin_addr) 将客户端的socket,ip保存到g_fdMap中
AddAuthConnToList(aconn) 将客户端的AuthConn保存到g_fdMap中
AuthConnRecv(fd, buf, used, size - used, 0) 接收客户端的认证报文并保存
ParsePacketHead(buf, processed, used - processed, size) 解析报文头部
OnDataReceived(conn, pkt, buf + processed) 处理接收报文,当头部module为MODULE_AUTH_SDK时调用AuthInterfaceOnDataReceived,其他则调用OnModuleMessageReceived
AuthInterfaceOnDataReceived(conn, pkt->module, pkt->seq, data, pkt->dataLen) 执行设备认证
DecryptMessage(pkt->module, data, pkt->dataLen) 解密报文体
OnModuleMessageReceived(conn, pkt->module, pkt->flags, pkt->seq, msg) 获取设备其他信息,如会话端口
BusGetLocalDeviceInfo() 返回g_deviceInfo
MsgVerifyIpUnPack(request, &connInfo, conn) 接收报文验证;获取公共版本,对端设备名称,设备类型,认证端口,会话端口,保存到connInfo中
MsgVerifyIpPack(&connInfo, localDevInfo, g_authPort, g_sessionPort) 构造Json对象,填入自身的CODE/版本/认证端口/会话端口/CONN_CAP/设备名称/设备类型/设备ID
AuthConnPostMessage(conn->fd, MODULE_CONNECTION, FLAG_REPLY, seq, reply) 发送回复报文(内容加密)

会话管理服务器

会话管理服务器用于数据传输。进行数据传输之前需要如下条件:1)客户端与服务器做设备认证,建立认证会话密钥;2)服务器需要调用CreateSessionServer函数,来设置用于响应某个session的处理函数。
会话管理服务器处理流程如下。

SelectSessionLoop
ProcessConnection
ProcessSesssionData
ProcessConnection
ProcessSesssionData
listener->onSessionOpened
ResponseToClient
GetSessionListenerByName
AssignValue2Session
TransFirstPkg2Json
接收客户端数据
新连接
listener->onBytesReceived
TcpSessionRecv
GetSessionListenerByName
为session的deviceId/fd赋值
CreateTcpSession
accept
AddSession

主要函数说明:

函数名 功能说明
SelectSessionLoop(tsm) 会话管理服务器处理线程
ProcessConnection(tsm) 处理会话连接请求
ProcessSesssionData(tsm, rfds) 处理会话数据
CreateTcpSession() 创建TcpSession对象并赋初置(sessionName为"softbus_Lite_unknown")
AddSession(tsm, session) 将TcpSession对象保存到tsm的sessionMap_中;此场景stm为g_sessionMgr
TransFirstPkg2Json(data, dataLen + AUTH_PACKET_HEAD_SIZE) 解析报文体为json对象并返回
AssignValue2Session(session, receiveObj) 从json对象中获取sessionName和sessionKey,保存到session对象中
GetSessionListenerByName(session->sessionName, strlen(session->sessionName)) 根据sessionName获取对应的sessionListener
ResponseToClient(session) 向客户端发送回复报文
TcpSessionRecv(session, buf, RECIVED_BUFF_SIZE, 0) 接收客户端报文,保存到buf中

对外接口

设备发现接口

源码foundation/communication/softbus_lite/interfaces/kits/discovery/discovery_service.h定义了设备发现的对外函数接口和数据结构。
函数接口如下

函数声明 参数说明 功能说明
int PublishService(const char moduleName, const struct PublishInfo info, const struct IPublishCallback *cb) 1)moduleName:服务名称,长度不大于63;2)info:服务的其他信息;3)cb:回调函数;4)返回值,0表示成功,-1 表示失败 发布服务
int UnPublishService(const char *moduleName, int publishId) 1)moduleName:服务名称,长度不大于63;2)publishId:服务ID;3)返回值,0表示成功,非0表示失败 删除已发布的服务
int SetCommonDeviceInfo(const struct CommonDeviceInfo *devInfo, unsigned int num) 1)devInfo:设备信息数组;2)num:设备信息数目;3)返回值,0表示成功,非0表示失败 设置设备信息,如设备名称,设备ID,设备类型等信息

数据结构如下

typedef enum {
    /** Automatic medium selection */
    AUTO = 0,
    /** Bluetooth */
    BLE = 1,
    /** Wi-Fi */
    COAP = 2,
    /** USB */
    USB = 3,
} ExchangeMedium;
typedef enum {
    /** Low */
    LOW = 0,
   /** Medium */
    MID = 1,
   /** High */
    HIGH = 2,
   /** Super-high */
    SUPER_HIGH = 3,
} ExchangeFreq;
typedef struct PublishInfo {
  /** Service publishing ID */
    int publishId;
  /** Service publishing mode, which can be {@link DISCOVER_MODE_PASSIVE} or {@link DISCOVER_MODE_ACTIVE } */
    int mode;
  /** Service publishing medium */
    ExchangeMedium medium;
  /** Service publishing frequency */
    ExchangeFreq freq;
  /** Service publishing capabilities. For details, see {@link g_capabilityMap}. */
    const char *capability;
  /** Capability data for service publishing */
    unsigned char *capabilityData;
  /** Maximum length of the capability data for service publishing (2 bytes) */
    unsigned int dataLen;
} PublishInfo;

typedef enum {
   /** Unsupported medium */
    PUBLISH_FAIL_REASON_NOT_SUPPORT_MEDIUM = 1,
   /** Invalid parameter */
    PUBLISH_FAIL_REASON_PARAMETER_INVALID = 2,
   /** Unknown reason */
    PUBLISH_FAIL_REASON_UNKNOWN = 0xFF
} PublishFailReason;
typedef struct IPublishCallback {
    /** Callback for successful publishing */
    void (*onPublishSuccess)(int publishId);
    /** Callback for failed publishing */
    void (*onPublishFail)(int publishId, PublishFailReason reason);
} IPublishCallback;

typedef enum {
    /** Device ID. The value contains a maximum of 64 characters. */
    COMM_DEVICE_KEY_DEVID = 0,
    /** Device type. Currently, only ddmpCapability is supported. */
    COMM_DEVICE_KEY_DEVTYPE = 1,
    /** Device name. The value contains a maximum of 63 characters. */
    COMM_DEVICE_KEY_DEVNAME = 2,
    /** Reserved */
    COMM_DEVICE_KEY_MAX
} CommonDeviceKey;
typedef struct CommonDeviceInfo {
    /** Device information type. For details, see {@link CommonDeviceKey}. */
    CommonDeviceKey key;
    /** Content to set */
    const char *value;
} CommonDeviceInfo;

数据传输接口

源码foundation/communication/softbus_lite/interfaces/kits/transport/sesion.h定义了数据传输的对外函数接口和数据结构。
函数接口如下

函数声明 参数说明 功能说明
int CreateSessionServer(const char *mouduleName, const char *sessionName, struct ISessionListener *listener) 1)mouduleName 服务名,长度不大于64;2)会话名,长度不大于64;3)listener 回调函数;4)返回值,0表示成功,-1表示失败 根据服务名和会话名创建一个会话服务器
int RemoveSessionServer(const char *mouduleName, const char *sessionName) 1)mouduleName 服务名,长度不大于64;2)会话名,长度不大于64;3)返回值,0表示成功,-1表示失败 按服务名和会话名检索,移除该会话服务器
int SendBytes(int sessionId, const unsigned char *data, unsigned int len) 1)sessionId 会话ID;2)data 发送数据buffer;3)len 发送数据长度,不能大雨984;4)返回值,0表示成功,-1表示失败 向sessionId表示的连接发送数据
int GetMySessionName(int sessionId, char *sessionName, unsigned int len) 1)sessionId 会话ID;2)sessionName 会话名称接收buffer;3)len 会话名称接收buffer大小;4)返回值,0表示成功,-1表示失败 由sessionId获取会话名
int GetPeerSessionName(int sessionId, char *sessionName, unsigned int len) 1)sessionId 会话ID;2)sessionName 会话名称接收buffer;3)len 会话名称接收buffer大小;4)返回值,0表示成功,-1表示失败 由sessionId获取会话对端的会话名
int GetPeerDeviceId(int sessionId, char *devId, unsigned int len) 1)sessionId 会话ID;2)devId 设备ID接收buffer;3)len 设备ID接收buffer大小;4)返回值,0表示成功,-1表示失败 由sessionId获取会话对端的设备ID
void CloseSession(int sessionId) 1)sessionId 会话ID 关闭sessionId对应的会话连接

数据结构如下

struct ISessionListener {
    /**@brief Called when a session is opened.
     * This function can be used to verify the session or initialize resources related to the session.
     * @param sessionId Indicates the session ID.
     * @return Returns 0 if the session connection is accepted; returns a non-zero value
     * otherwise (you do not need to call {@link CloseSession} to close the session).
     * @since 1.0
     * @version 1.0
     */
    int (*onSessionOpened)(int sessionId);
    /**
     * @brief Called when a session is closed.
     * This function can be used to release resources related to the session.
     * You do not need to call {@link CloseSession}.
     * @param sessionId Indicates the session ID.
     * @since 1.0
     * @version 1.0
     */
    void (*onSessionClosed)(int sessionId);
    /**
     * @brief Called when data is received.
     * This function is used to notify that data is received.
     * @param sessionId Indicates the session ID.
     * @param data Indicates the pointer to the data received.
     * @param dataLen Indicates the length of the data received.
     * @since 1.0
     * @version 1.0
     */
    void (*onBytesReceived)(int sessionId, const void *data, unsigned int dataLen);
};

使用示例

标准使用方法

客户端 服务器 PublishService 客户端向服务器5684端口发送设备信息 服务器向客户端5684端口发送自身设备信息/认证端口 客户端向服务器认证端口发送设备信息/密钥信息 服务器回复自身设备信息/密钥信息 客户端向服务器认证端口发送验证IP消息 服务器回复自身会话端口 CreateSessionServer 客户端向服务器会话端口发送会话密钥 服务器响应 客户端向服务器会话端口发送消息 服务器响应 客户端 服务器

测试方法

由于对密钥交换,认证会话密钥的生成流程不清楚,要测试softbus_lite的数据传输功能,需要屏蔽与加解密相关的操作,也需要coap server将认证端口直接暴露出来。

数据传输报文(未加密)格式如下

序号 字段 偏移 长度 含义
1 identified 0 4 识别码,用0xBABEFACE
2 module 4 4 模块代码,可以不管
3 seq 8 8 报文序号
4 flags 16 4 flags,可以不管
5 datalen 20 4 payload长度,以字节为单位
6 payload 24 n 报文体

数据传输报文(认证密钥加密)格式如下

序号 字段 偏移 长度 含义
1 identified 0 4 识别码,用0xBABEFACE
2 module 4 4 模块代码,可以不管
3 seq 8 8 报文序号
4 flags 16 4 flags,可以不管
5 datalen 20 4 payload长度,以字节为单位
6 authkey-index 24 4 认证会话对应的索引
7 iv 28 12 iv
8 encrypt-playload 40 n 报文体
9 tag 40+n 16 tag

数据传输报文(会话密钥加密)格式如下

序号 字段 偏移 长度 含义
1 identified 0 4 识别码,用0xBABEFACE
2 module 4 4 模块代码,可以不管
3 seq 8 8 报文序号
4 iv 28 12 iv
5 encrypt-playload 40 n 报文体
6 tag 40+n 16 tag

测试1,设备发现。目标:获得认证端口与会话端口。
步骤如下。

  1. 服务端调用PublishService发布一个服务"softbus-server"
  2. 客户端向服务器5684端口发送coap发现请求
  3. 客户端收到服务器的返回报文后,确认该报文中含有服务器的设备信息,认证端口和会话端口

测试2,回声服务器(回声服务器是将收到的报文原封不动的发送回去)。目标:验证数据传输通路正确;
步骤如下。

  1. 服务端调用PublishService发布一个服务"softbus-server"
  2. 服务端调用CreateSessionServer创建一个会话服务器"echo-server"
  3. 客户端向服务器5684端口发送coap发现请求
  4. 客户端收到服务器的返回报文后,确认该报文中含有服务器的设备信息,认证端口和会话端口
  5. 客户端连接服务器会话端口
  6. 客户端向服务器会话端口发送报文后,服务器回复后,确认该报文中是否含有服务器的设备信息
  7. 客户端向服务器会话端口发送报文后,服务器回复后,确认该报文报文体与发送报文体一样

你可能感兴趣的:(鸿蒙系统,harmonyos)