概述
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)时,服务器会将自身的会话端口返回给客户端。
从上可以看出,认证服务器主要工作有两个:
- 认证连接维护:接收客户端认证连接,记录到g_fdMap;
- 响应认证数据:解析客户端的认证数据,解析,并作相应处理;
主要函数说明:
函数名 | 功能说明 |
---|
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 <b>ddmpCapability</b> 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 <b>0</b> 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,设备发现。目标:获得认证端口与会话端口。 步骤如下。
- 服务端调用PublishService发布一个服务"softbus-server"
- 客户端向服务器5684端口发送coap发现请求
- 客户端收到服务器的返回报文后,确认该报文中含有服务器的设备信息,认证端口和会话端口
测试2,回声服务器(回声服务器是将收到的报文原封不动的发送回去)。目标:验证数据传输通路正确; 步骤如下。
- 服务端调用PublishService发布一个服务"softbus-server"
- 服务端调用CreateSessionServer创建一个会话服务器"echo-server"
- 客户端向服务器5684端口发送coap发现请求
- 客户端收到服务器的返回报文后,确认该报文中含有服务器的设备信息,认证端口和会话端口
- 客户端连接服务器会话端口
- 客户端向服务器会话端口发送报文后,服务器回复后,确认该报文中是否含有服务器的设备信息
- 客户端向服务器会话端口发送报文后,服务器回复后,确认该报文报文体与发送报文体一样
|