softbus_lite组件是在受限设备上提供数据传输服务,除此之外,还提供设备发现,设备认证,通信安全功能。
softbus_lite由coap服务器,消息队列处理线程,设备认证服务器,会话管理服务器四部分组成。其中coap服务器是UDP服务器,端口为5684,用于设备发现。消息队列用于内部消息传递,目前仅用在创建设备认证服务器和会话管理服务器上,不对外提供服务。设备认证服务器是TCP服务器,其端口不固定,用于设备认证,密钥协商。会话管理服务器是TCP服务器,其端口不固定,用于数据传输。
softbus_lite内部结构图
softbus_lite支持网口和Wifi两种通信方式。
PublishService函数的功能是创建coap服务器,消息队列处理线程,设备认证服务器,会话管理服务器,声明一个功能模块。只有调用了PublishService函数,softbus_lite才能向外提供服务。
PublishService函数处理流程如下。
由于CoapInitDiscovery函数已打开消息队列处理线程,而CoapWriteMsgQueue(UPDATE_IP_EVENT)函数向消息队列发送了消息,将触发消息队列处理流程,将调用g_wifiCallback(para),这时g_wifiCallback为WifiEventTrigger,para为1,所以处理流程如下。
主要函数说明:
函数名 | 功能说明 |
---|---|
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服务器用于响应设备发现,检查接收报文是否合法,向外部设备提供自身的设备名称,设备ID,版本,模式,认证端口,IP地址,能力值等信息。其处理流程如下。
主要函数说明:
函数名 | 功能说明 |
---|---|
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端口 |
设备认证服务器用于设备认证,建立认证会话密钥,向外提供会话端口。其处理流程如下。
由于AuthInterfaceOnDataReceived函数主要处理流程在base/security/deviceauth目录下,这里不再展开。
OnModuleMessageReceived函数处理流程如下:
可见,如果客户端发送报文体中CODE域值为CODE_VERIFY_IP(即为1)时,服务器会将自身的会话端口返回给客户端。
从上可以看出,认证服务器主要工作有两个:
主要函数说明:
函数名 | 功能说明 |
---|---|
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(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);
};
由于对密钥交换,认证会话密钥的生成流程不清楚,要测试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,设备发现。目标:获得认证端口与会话端口。
步骤如下。
测试2,回声服务器(回声服务器是将收到的报文原封不动的发送回去)。目标:验证数据传输通路正确;
步骤如下。