tcp_session_manager.c代码分析上篇
本篇主要分析tcp_session_manager.h和部分tcp_session_manager.c代码
一、背景知识
传送门(点击查看对应理论知识)
二、代码分析
1.头文件tcp_session_manager.h
#ifndef TCP_SESSION_MANAGER_H
#define TCP_SESSION_MANAGER_H
//条件编译避免重复编译
#include "comm_defs.h"
int CreateTcpSessionMgr(bool asServer, const char* localIp);
int RemoveTcpSessionMgr(void);
//声明函数
#endif // TCP_SESSION_MANAGER_H
2.头文件comm_defs.h
#ifndef COMM_DEFS_H
#define COMM_DEFS_H
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "securec.h"
#include <unistd.h>
#define COMM_UNUSED __attribute__((unused))
//表示可能没有使用,告诉编译器在编译此类型函数或变量时不需报警告。
#define COMM_EXPORT __attribute__((visibility("default")))
//用来避免编译时的外部链接混乱,忽视警告
#endif // COMM_DEFS_H
3.源文件tcp_session_manager.c
1.头文件引用和宏定义
#include "tcp_session_manager.h"
#include <arpa/inet.h>
#if defined(__LITEOS_M__) || defined(__LITEOS_RISCV__)
#include <cmsis_os.h>
#else
#include <pthread.h>
#include "pms_interface.h"
#include "pms_types.h"
#endif
#include <malloc.h>
#include <mbedtls/base64.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include "aes_gcm.h"
#include "auth_conn.h"
#include "auth_interface.h"
#include "bus_manager.h"
#include "cJSON.h"
#include "message.h"
#include "os_adapter.h"
#include "session.h"
#include "tcp_session.h"
#include "tcp_socket.h"
#include "trans_lock.h"
#include "wifi_auth_manager.h"
#define MAX_SESSION_NUM 3 //会话最大值
#define MAX_SESSION_SERVER_NUM 8 //会话服务器的最大数量
#define MAX_SESSION_SUM_NUM (MAX_SESSION_NUM * MAX_SESSION_SERVER_NUM)
#define MODULE_SESSION 6
#define AUTH_PACKET_HEAD_SIZE 24
#define TRANS_PACKET_HEAD_SIZE 16
#define TRANS_SEQ_NUM_OFFSET 8
#define RECIVED_BUFF_SIZE (4 * 1024)
#define LISTEN_BACKLOG 4
#define DEFAULT_SEQNUM 100
#define DEFAULT_TV_SEC 10
#define DEFAULT_TRANS_PORT 0
#define DEFAULT_API_VERSION 2
#define DEFAULT_LONG_LEN 8
#define SESSION_KEY_INDEX_SIZE 4
#define TRANS_FAILED (-1)
#define DEFAULT_TIMEOUT 200
#define ONE_SEC 1000
#define SIZE_OF_INT sizeof(int)
#define SIZE_OF_LONG_LONG sizeof(long long)
#define SEND_BUFF_MAX_SIZE (RECIVED_BUFF_SIZE - SESSION_KEY_INDEX_SIZE - TRANS_PACKET_HEAD_SIZE - OVERHEAD_LEN)
#define SOFTBUS_PERMISSION_NAME "ohos.permission.DISTRIBUTED_DATASYNC"
2. 结构体的定义
typedef struct {
char moduleName[NAME_LENGTH];//模块名
char sessionName[NAME_LENGTH];//会话名
struct ISessionListener *listener;//绘画监听
} SessionListenerMap;
//绘画监听结构体
typedef struct {
bool asServer;
int listenFd; //监听描述符(套接字)
bool isSelectLoopRunning;
struct ISessionListener *callback_; //回调变量
TcpSession* sessionMap_[MAX_SESSION_SUM_NUM];//会话数组
SessionListenerMap* serverListenerMap[MAX_SESSION_SERVER_NUM];//服务器监听数组
} TcpSessionMgr;
//定义tcp会话管理
3.函数定义
下面两个函数通过sessionid来获取session,以及通过名字来获取会话的监听listener
TcpSessionMgr* g_sessionMgr;
static TcpSession *GetSessionById(int sessionId)//获取会话
{
if (g_sessionMgr == NULL) {
return NULL;
}
for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
if ((g_sessionMgr->sessionMap_[i] != NULL) && (g_sessionMgr->sessionMap_[i]->fd == sessionId)) {
//g_sessionMgr->sessionMap_[i]不为空并且该元素的套接字描述符不为空,就返回g_sessionMgr->sessionMap_[i]
return g_sessionMgr->sessionMap_[i];
}
}
return NULL;
}//该函数通过会话id来获取会话
static SessionListenerMap *GetSessionListenerByName(const char *sessionName, int nameSize)
//该函数通过名字来获取会话的监听
{
if (nameSize <= 0 || nameSize >= NAME_LENGTH) {
SOFTBUS_PRINT("[TRANS] GetSessionListenerByName invalid para\n");
return NULL;
//检查传入参数问题
}
if (g_sessionMgr == NULL) {
return NULL;
}//检查g_sessionMgr是否空值
SessionListenerMap* sessionListener = NULL;
for (int i = 0; i < MAX_SESSION_SERVER_NUM; i++) {
if (g_sessionMgr->serverListenerMap[i] != NULL &&
strcmp(g_sessionMgr->serverListenerMap[i]->sessionName, sessionName) == 0) {
sessionListener = g_sessionMgr->serverListenerMap[i];
//g_sessionMgr->serverListenerMap[i]不为空并且该结构体中sessionName和传入参数相同的话,
//就获取对应的会话监听结构体
break;
}
}
return sessionListener;
}
初始化所选择的列表,将会话结构体数组中fd不断更新到所传入的两个套接字集合当中
static int InitSelectList(const TcpSessionMgr *tsm, fd_set *rfds, fd_set *exceptfds)
//参数为一个tcp会话管理,两套套接字描述符
{
if (tsm == NULL || tsm->listenFd == -1 || rfds == NULL) {
return TRANS_FAILED;
//检查函数传入参数
}
int maxFd = tsm->listenFd;
FD_ZERO(rfds);
FD_ZERO(exceptfds);
//清空,初始化两个套接字集合
FD_SET(tsm->listenFd, rfds);//用于在文件描述符集合中增加一个新的文件描述符
if (GetTcpMgrLock() != 0) {
return TRANS_FAILED;
//创建/获取一个互斥锁
}
//对数据进行访问等操作时,申请互斥锁。操作完后释放互斥锁
for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
if ((tsm->sessionMap_[i]) == NULL) {
continue;
}
int fd = tsm->sessionMap_[i]->fd;
if (fd >= 0) {
FD_SET(fd, rfds);
FD_SET(fd, exceptfds);
maxFd = (maxFd > fd) ? maxFd : fd;//用来更新最大fd
}
}//遍历(tsm->sessionMap结构体中所有元素,将各个符合条件的套接字添加进rfds和exceptfds
if (ReleaseTcpMgrLock() != 0) {
return TRANS_FAILED;
//释放互斥锁
}
return maxFd;
}
初始化会话管理,申请空间并将所有的会话数组遍历置空
static int InitGSessionMgr(void)
{
if (g_sessionMgr != NULL) {
return 0;
}
g_sessionMgr = malloc(sizeof(TcpSessionMgr));//申请空间
if (g_sessionMgr == NULL) {
return TRANS_FAILED;
}
(void)memset_s(g_sessionMgr, sizeof(TcpSessionMgr), 0, sizeof(TcpSessionMgr));
//将(g_sessionMgr相关指定空间内的元素置为0
g_sessionMgr->listenFd = -1;
g_sessionMgr->isSelectLoopRunning = false;
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;
}
//将所有服务器的监听元素也置为空
return 0;
}
RemoveSession函数用来删除指定会话,释放该会话相关的所有空间,实现删除的目的
static bool RemoveSession(TcpSessionMgr *tsm, int sessionId)//删除会话
{
if (tsm == NULL || sessionId < 0) {
SOFTBUS_PRINT("[TRANS] RemoveSession invalid para\n");
return false;
//检查函数传入参数
}
SessionSeqNumNode* node = NULL;
List* pos = NULL;
List* tmp = NULL;
for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
if (tsm->sessionMap_[i] != NULL && tsm->sessionMap_[i]->fd == sessionId) {
LIST_FOR_EACH_SAFE(pos, tmp, tsm->sessionMap_[i]->seqNumList) {
node = (SessionSeqNumNode*)pos;
//遍历整个链表
if (node == NULL) {
continue;
}
free(node);
//释放node空间
}
free(tsm->sessionMap_[i]->seqNumList);
free(tsm->sessionMap_[i]);
//-释放申请的空间
tsm->sessionMap_[i] = NULL;
return true;
}
}
//该函数通过释放空间来达到删除会话的作用
return false;
}
CloseTransSession函数用来关闭数据传输session会话
static void CloseTransSession(int sessionId)//关闭trans传输会话
{
CloseFd(sessionId);//根据sessionid来关闭会话
TcpSession *session = GetSessionById(sessionId);//通过id来获取会话
if (session == NULL) {
SOFTBUS_PRINT("[TRANS] CloseTransSession GetSessionById fail\n");
return;
//检查会话是否为空
}
SessionListenerMap *serverListener = GetSessionListenerByName(session->sessionName, strlen(session->sessionName));
//通过session名字来获取服务器监听
if (serverListener == NULL) {
return;
}
serverListener->listener->onSessionClosed(sessionId);
RemoveSession(g_sessionMgr, sessionId);
//通过对应的sessionid和g_sessionMgr来关闭session
}
RemoveExceptSessionFd每一个在exceptfds当中的元素对应的session全部关闭,并在exceptfds套接字集合当中删除session对应的fd
static int RemoveExceptSessionFd(const TcpSessionMgr *tsm, fd_set *exceptfds)//去除套接字
{
if (GetTcpMgrLock() != 0) {
return TRANS_FAILED;
//申请互斥锁
}
if (g_sessionMgr == NULL || g_sessionMgr->listenFd == -1 || exceptfds == NULL) {
ReleaseTcpMgrLock();
return TRANS_FAILED;
//检查参数,如果检查符合要求就释放互斥锁
}
if (g_sessionMgr->listenFd >= 0 && FD_ISSET(g_sessionMgr->listenFd, exceptfds)) {
ReleaseTcpMgrLock();
return TRANS_FAILED;
//
}
int result = TRANS_FAILED;
for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
if ((tsm->sessionMap_[i]) == NULL) {
continue;
}
int fd = tsm->sessionMap_[i]->fd;
if (fd >= 0) {
if (FD_ISSET(fd, exceptfds)) {//来检查fd是否在exceptfds集合当中
result = 0;
SOFTBUS_PRINT("Session is closed. %d\r\n", fd);
CloseTransSession(fd);
}
}
//将每一个在exceptfds当中的元素对应的session全部关闭
}
if (ReleaseTcpMgrLock() != 0) {
return TRANS_FAILED;
//检查是否释放互斥锁
}
return result;
}
AddSession传入会话管理变量和session,将session添加进会话管理变量。
static bool AddSession(TcpSessionMgr *tsm, TcpSession *session)//添加会话
{
if (tsm == NULL || session == NULL) {
SOFTBUS_PRINT("[TRANS] AddSession invalid para\n");
return false;
//检查擦传入参数是否为空
}
for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
if (tsm->sessionMap_[i] == NULL) {
//检查数组中的空位置,并存进会话
tsm->sessionMap_[i] = session;
return true;
}
}
return false;
}
ProcessConnection通过给定的tcp会话管理,accept函数实现和客户端的连接,并且通过GetOnLineAuthConnByIp来获取用户线上连接信息,将所连接设备的id存入session,并添加进会话管理变量当中,以此来实现进程连接。
//进程链接函数
static void ProcessConnection(TcpSessionMgr *tsm)
{
struct sockaddr_in addr = { 0 };
socklen_t addrLen = sizeof(addr);
int cfd = accept(tsm->listenFd, (struct sockaddr *)&addr, &addrLen);
//accept函数指定服务端去接受客户端的连接,接收后,返回了客户端套接字的标识,且获得了客户端套接字的“地方”
if (cfd < 0) {
SOFTBUS_PRINT("[TRANS] ProcessConnection accept fail\n");
//cfd小于零说明函数返回失败,accept失败
return;
}
TcpSession *session = CreateTcpSession();
if (session == NULL) {
SOFTBUS_PRINT("[TRANS] ProcessConnection CreateTcpSession fail, fd = %d\n", cfd);
CloseSession(cfd);
//创建的tcpsession为空的话。关闭cfd对应的会话
return;
}
AuthConn* authConn = GetOnLineAuthConnByIp(inet_ntoa(addr.sin_addr));
if (authConn != NULL && strncpy_s(session->deviceId, MAX_DEV_ID_LEN, authConn->deviceId,
strlen(authConn->deviceId)) != 0) {
//用来将用户连接的设备id复制到会话的设备id
SOFTBUS_PRINT("[TRANS] Error on copy deviceId of session.");
free(session);
CloseSession(cfd);
//释放空间,关闭会话
return;
}
session->fd = cfd;
int result = AddSession(tsm, session);
//将session添加进tsm会话
if (result == false) {
SOFTBUS_PRINT("[TRANS] AddSession fail\n");
free(session);
CloseSession(cfd);
return;
}//检查是否添加成功
return;
}
下面的代码用来打包数据并且获取信息、并对相关信息进行处理。其中GetReplyMsg用来获取回复,GetIntFromBuf和GetKeyIndex都用来初始化通过复制信息来实现个数组赋值。
static unsigned char* PackBytes(const char *str, int *bufLen)//打包数据
{
return AuthConnPackBytes(MODULE_SESSION, 1, DEFAULT_SEQNUM, str, bufLen);
//将数据封装打包的函数。确定数据不为空,确定是否为密文,确定总长度,将信息打包安全拷贝到data中,如果是密文将密钥同样打包入数据中,最后返回data指针指向的数据buf
}
static void GetReplyMsg(cJSON *jsonObj, const TcpSession *session)//用来获取回复消息
{
cJSON_AddNumberToObject(jsonObj, "CODE", 1);
cJSON_AddNumberToObject(jsonObj, "API_VERSION", DEFAULT_API_VERSION);
//cjson中语法,添加数字
DeviceInfo *local = BusGetLocalDeviceInfo();
//获取设备信息检查设备是否有效
if (local == NULL) {
return;
}
char* deviceId = local->deviceId;
cJSON_AddStringToObject(jsonObj, "DEVICE_ID", deviceId);
cJSON_AddNumberToObject(jsonObj, "UID", -1);
cJSON_AddNumberToObject(jsonObj, "PID", -1);
cJSON_AddStringToObject(jsonObj, "PKG_NAME", session->sessionName);
cJSON_AddStringToObject(jsonObj, "CLIENT_BUS_NAME", session->sessionName);
cJSON_AddStringToObject(jsonObj, "AUTH_STATE", "");
cJSON_AddNumberToObject(jsonObj, "CHANNEL_TYPE", 1);
//对有效设备相关信息进行更新
}
static int GetIntFromBuf(const char *buf, int offset)//用来将buf中内容以offset的偏置进行复制信息
{
int val = 0;
if (memcpy_s(&val, SIZE_OF_INT, buf + offset, SIZE_OF_INT) != 0) {
//memcpy将源内存地址以指定位置开始,指定长度开始复制到另一个数组中
return TRANS_FAILED;
}
return val;
//返回新复制信息的数组
}
static int GetKeyIndex(const char *in, unsigned int inOffset, unsigned int indexLen)//获取索引key值的函数
{
int val = 0;
if (memcpy_s(&val, indexLen, in + inOffset, indexLen) != EOK) {
//复制信息
return -1;
}
return val;
}
//关闭所有会话
static void CloseAllSession(const TcpSessionMgr *tsm)
{
if (GetTcpMgrLock() != 0) {
return;
//申请一个互斥锁
}
for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
//遍历数组中所有会话,检查是否为空,不为空则关闭会话
if ((tsm->sessionMap_[i]) == NULL) {
continue;
}
CloseTransSession(tsm->sessionMap_[i]->fd);
//关闭数据传输会话
}
if (ReleaseTcpMgrLock() != 0) {
return;
}
//执行完所有操作释放互斥锁
}