鸿蒙源码分析(十二) 比眉伴天荒 2022-09-02 15:52 173阅读 0赞 # tcp\_session\_manager.c代码分析下篇 # 本篇主要分析tcp\_session\_manager.c中出现的函数 ## 一、相关链接 ## 和本篇代码相关的一些背景知识和该文件的上一部分代码详解: 1. [tcp\_socket机制详解][tcp_socket] 2. [互斥锁和消息队列详解][Link 1] 3. [tcp\_session详解][tcp_session] 4. [tcp\_session\_manager.c代码分析上篇][tcp_session_manager.c] 5. [tcp\_session\_manager.c代码分析中篇][tcp_session_manager.c 1] ## 二、代码分析 ## SelectSessionLoop函数用来处理会话线程数据 static void SelectSessionLoop(TcpSessionMgr *tsm) { if (tsm == NULL) { return; } SOFTBUS_PRINT("[TRANS] SelectSessionLoop begin\n"); tsm->isSelectLoopRunning = true; while (true) { fd_set readfds; fd_set exceptfds; int maxFd = InitSelectList(tsm, &readfds, &exceptfds); //将tsm中非空元素的套接字写进readfds集合和exceptfds集合 if (maxFd < 0) { break; } errno = 0; int ret = select(maxFd + 1, &readfds, NULL, &exceptfds, NULL); //select用来监视文件描述符的变化和状况 //参数一:最大的文件描述符加1。 //参数二:用于检查可读性, //参数三:用于检查可写性, //参数四:用于检查带外数据, //参数五:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。 if (ret < 0) { //ret<0说明执行失败 SOFTBUS_PRINT("RemoveExceptSessionFd\r\n"); if (errno == EINTR || RemoveExceptSessionFd(tsm, &exceptfds) == 0) { continue; } SOFTBUS_PRINT("[TRANS] SelectSessionLoop close all Session\n"); CloseAllSession(tsm); break; } else if (ret == 0) { //ret==0说明select超时 continue; } else { ProcessData(tsm, &readfds);//select执行成功就处理数据 } } tsm->isSelectLoopRunning = false; } CreateSessionServerInner用来通过模块名,会话名,服务器监听对象来创建一个内部服务器 static int CreateSessionServerInner(const char* moduleName, const char* sessionName, struct ISessionListener *listener) { //创建内部会话服务器 if (g_sessionMgr == NULL && InitGSessionMgr() != 0) { return TRANS_FAILED; //检查参数和是否初始化 } if (listener == NULL || sessionName == NULL || moduleName == NULL) { //检查函数传入的参数 SOFTBUS_PRINT("[TRANS] CreateSessionServer invalid param\n"); return TRANS_FAILED; } if (strlen(moduleName) >= NAME_LENGTH || strlen(sessionName) >= NAME_LENGTH) { SOFTBUS_PRINT("[TRANS] CreateSessionServerInner name length is too big\n"); return TRANS_FAILED; //检查会话名和模块名字的标准是否合格 } int findIndex = -1; for (int i = 0; i < MAX_SESSION_SERVER_NUM; i++) { if (g_sessionMgr->serverListenerMap[i] == NULL) { findIndex = i; break; //遍历所有会话服务器,找出空值对应下标 } } if (findIndex == -1) { return TRANS_FAILED; } if (findIndex >= 0 && findIndex < MAX_SESSION_SERVER_NUM) { g_sessionMgr->serverListenerMap[findIndex] = calloc(1, sizeof(SessionListenerMap)); if (g_sessionMgr->serverListenerMap[findIndex] == NULL) { return TRANS_FAILED; //对上次找出空值,将空值对应位置申请空间内存 } SessionListenerMap *listenerMap = g_sessionMgr->serverListenerMap[findIndex]; if (strncpy_s(listenerMap->sessionName, NAME_LENGTH, sessionName, strlen(sessionName)) || strncpy_s(listenerMap->moduleName, NAME_LENGTH, moduleName, strlen(moduleName))) { //将会话名二号模块名字复制到指定位置 free(listenerMap); listenerMap = NULL; return TRANS_FAILED; } listenerMap->listener = listener;//监听变量赋值 } return 0; } RemoveSessionServerInner通过模块名和会话名实现服务器的删除 static int RemoveSessionServerInner(const char* moduleName, const char *sessionName)//删除内部服务器 { if (g_sessionMgr == NULL) { return TRANS_FAILED; }//检查参数 int removeFd = -1; for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) { if (g_sessionMgr->sessionMap_[i] != NULL && strcmp(g_sessionMgr->sessionMap_[i]->sessionName, sessionName) == 0) { removeFd = g_sessionMgr->sessionMap_[i]->fd; //检查非空值释放会话空间并且关闭数据传输会话 CloseTransSession(removeFd); free(g_sessionMgr->sessionMap_[i]); g_sessionMgr->sessionMap_[i] = NULL; } } for (int i = 0; i < MAX_SESSION_SERVER_NUM; i++) { //遍历所有服务器 SessionListenerMap *serverListener = g_sessionMgr->serverListenerMap[i]; if (serverListener != NULL && (strcmp(serverListener->sessionName, sessionName) == 0) && //检查服务器监听是否开启,以及监听会话名和传入的会话名是否相同,监听的模块名和传入的模块名是否相同 (strcmp(serverListener->moduleName, moduleName) == 0)) { if (serverListener->listener != NULL && serverListener->listener->onSessionClosed != NULL) { serverListener->listener->onSessionClosed(removeFd);//关闭会话 } free(serverListener);//关闭服务器的监听 serverListener = NULL; break; } } return 0; } 该条件编译中第一部分函数已在前面出现过,主要用来处理线程数据 #if defined(__LITEOS_M__) || defined(__LITEOS_RISCV__) int StartSelectLoop(TcpSessionMgr *tsm) { if (tsm == NULL) { return TRANS_FAILED; } if (tsm->isSelectLoopRunning) { return 0; } osThreadId_t sessionLoopTaskId; osThreadAttr_t attr; attr.name = "trans_session_task"; attr.attr_bits = 0U; attr.cb_mem = NULL; attr.cb_size = 0U; attr.stack_mem = NULL; attr.stack_size = (LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE * 2); attr.priority = osPriorityNormal5; // LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO -> cmsis prio sessionLoopTaskId = osThreadNew((osThreadFunc_t)SelectSessionLoop, (void *)tsm, &attr); if (NULL == sessionLoopTaskId) { SOFTBUS_PRINT("[TRANS] StartSelectLoop TaskCreate fail\n"); return TRANS_FAILED; } return 0; } 该代码块主要用来创建线程,实现线程中数据处理 #else #define MIN_STACK_SIZE 0x8000 static pthread_key_t g_localKey = 0; typedef void *(*Runnable)(void *argv); typedef struct ThreadAttr ThreadAttr; struct ThreadAttr { const char *name; // 线程 uint32_t stackSize; // 堆栈大小 uint8_t priority; // 优先级 uint8_t reserved1; // reserved1 (must be 0)//可以看作是端口 uint16_t reserved2; // reserved2 (must be 0) }; typedef void *ThreadId; ThreadId TcpCreate(Runnable run, void *argv, const ThreadAttr *attr)//用来创建一个tcp相关的线程 { if (attr == NULL) { return NULL; } int ret; pthread_attr_t threadAttr; ret = pthread_attr_init(&threadAttr);//对线程进行初始化 if (ret != 0) { //初始化是否成功 SOFTBUS_PRINT("[TRANS] TcpCreate pthread attr init fail\n"); } ret = pthread_attr_setstacksize(&threadAttr, (attr->stackSize | MIN_STACK_SIZE)); //指定线程堆栈区的大小 if (ret != 0) { SOFTBUS_PRINT("[TRANS] TcpCreate pthread attr setstacksize fail\n");//分配大小是否成功 } struct sched_param sched = { attr->priority}; ret = pthread_attr_setschedparam(&threadAttr, &sched);//用于设置线程的调用策略和优先级,第一个参数为线程ID,第二个参数优先级 if (ret != 0) { SOFTBUS_PRINT("[TRANS] TcpCreate pthread attr setschedparam fail\n"); //检查是否优先级设置失败 } ret = pthread_key_create(&g_localKey, NULL);//对线程池中找到数据分配至g_localKey,第二个参数为清理函数。NULL表示选择默认的数据清理函数 if (ret != 0) { SOFTBUS_PRINT("[TRANS] TcpCreate pthread key create fail\n"); } pthread_t threadId = 0; ret = pthread_create(&threadId, &threadAttr, run, argv);//创建一个线程 /*第一个参数为指向线程 标识符的指针。 第二个参数用来设置线程属性。 第三个参数是线程运行函数的起始地址。 最后一个参数是运行函数的参数。*/ if (ret != 0) { return NULL; } return (ThreadId)threadId;//最后返回线程的ID } int StartSelectLoop(TcpSessionMgr *tsm)//线程处理所有的会话数据接收 { if (tsm == NULL) { return TRANS_FAILED; //检查参数 } if (tsm->isSelectLoopRunning) { return 0; //检查tsm的SelectLoop状态是否运行 } ThreadAttr attr = { "tcp", 0x800, 20, 0, 0}; register ThreadId threadId = (ThreadId)TcpCreate((Runnable)SelectSessionLoop, tsm, &attr); //创建并定义了一个寄存器线程变量 if (threadId == NULL) { return TRANS_FAILED; } tsm->isSelectLoopRunning = true;//线程处理中 } #endif CreateTcpSessionMgr函数和RemoveTcpSessionMgr实现TcpSessionMgr的创建和删除,参数为服务器和本地ip int CreateTcpSessionMgr(bool asServer, const char* localIp)//传入一个服务器和本地ip来创建一个tcp会话管理器 { if (localIp == NULL) { //检查传入的ip参数是否为空 return TRANS_FAILED; } if (InitTcpMgrLock() != 0 || GetTcpMgrLock() != 0) { return TRANS_FAILED; //初始化mutex相关锁和互斥体。申请互斥锁 } int ret = InitGSessionMgr(); if (ReleaseTcpMgrLock() != 0 || ret != 0) { FreeSessionMgr(); //初始化GSessionMgr失败iu释放互斥锁,函数退出 return TRANS_FAILED; } g_sessionMgr->asServer = asServer; int listenFd = OpenTcpServer(localIp, DEFAULT_TRANS_PORT);//根据端口和ip打开服务器,并将套接字写进listenfd if (listenFd < 0) { SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr OpenTcpServer fail\n"); //检查服务器是否打开成功 FreeSessionMgr(); return TRANS_FAILED; } int rc = listen(listenFd, LISTEN_BACKLOG);//根据打开的tcp服务器进行监听 if (rc != 0) { SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr listen fail\n"); CloseSession(listenFd); FreeSessionMgr(); return TRANS_FAILED; } g_sessionMgr->listenFd = listenFd;//将服务器的套接字写进g_sessionMgr->listenFd signal(SIGPIPE, SIG_IGN);//使用函数来处理信号SIGPIPE,SIG_IGN为忽视信号函数 if (StartSelectLoop(g_sessionMgr) != 0) { //使用线程处理g_sessionMgr中数据 SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr StartSelectLoop fail\n"); CloseSession(listenFd); FreeSessionMgr(); return TRANS_FAILED; } return GetSockPort(listenFd); //返回listenFd对应套接字的socket端口 } int RemoveTcpSessionMgr(void)//删除TcpSessionMgr { if (g_sessionMgr == NULL) { return TRANS_FAILED;//检查g_sessionMgr } CloseAllSession(g_sessionMgr); //关闭g_sessionMgr相关所有session if (GetTcpMgrLock() != 0) { //申请互斥锁 return TRANS_FAILED; } CloseFd(g_sessionMgr->listenFd);//关闭g_sessionMgr->listenFd对应文件设备 g_sessionMgr->listenFd = -1;//将listenfd改为-1,相当于没有所连接的listenfd if (ReleaseTcpMgrLock() != 0) { return TRANS_FAILED; //释放互斥锁。 } return 0; } CreateSessionServer函数和RemoveSessionServer为创建和删除会话服务器的函数 通过会话名,模块名,监听名 int CreateSessionServer(const char* moduleName, const char* sessionName, struct ISessionListener *listener)//创建会话服务器 { if (moduleName == NULL || sessionName == NULL || listener == NULL) { return TRANS_FAILED;//检查参数 } if (SoftBusCheckPermission(SOFTBUS_PERMISSION_NAME) != 0) { return TRANS_FAILED;//检查权限 } if (GetTcpMgrLock() != 0) { return TRANS_FAILED;//申请互斥锁,一边操作数据文件设备 } int ret = CreateSessionServerInner(moduleName, sessionName, listener);//根据moduleName, sessionName, listener创建一个内部的会话服务器 if (ReleaseTcpMgrLock() != 0) { return TRANS_FAILED; //释放互斥锁 } return ret; } int RemoveSessionServer(const char* moduleName, const char *sessionName)//删除会话服务器 { if (moduleName == NULL || sessionName == NULL) { SOFTBUS_PRINT("[TRANS] moduleName == NULL || sessionName == NULL\n"); return TRANS_FAILED;//检查参数 } if (SoftBusCheckPermission(SOFTBUS_PERMISSION_NAME) != 0) { return TRANS_FAILED;//检查用户的权限 } if (GetTcpMgrLock() != 0) { return TRANS_FAILED;//申请加互斥锁 } int ret = RemoveSessionServerInner(moduleName, sessionName);//删除内部的会话服务器 if (ReleaseTcpMgrLock() != 0) { return TRANS_FAILED;//释放锁 } return ret; } SendBytes和TransPackBytes两个函数实现数据传输并加密。将session会话中sendData中数据发送带连接另一端的socket,大小为sendDataLen。传输依赖于ace\_gcm.c文件中EncryptTransData函数,实现数据的传输和加密。 static unsigned char *TransPackBytes(TcpSession *session, const unsigned char *sendData, unsigned int sendDataLen, int *bufLen) { if (session == NULL || sendData == NULL || bufLen == NULL || sendDataLen > SEND_BUFF_MAX_SIZE || sendDataLen == 0) { return NULL;//检查传入参数 } int allLen = sendDataLen + TRANS_PACKET_HEAD_SIZE + OVERHEAD_LEN; if (allLen > RECIVED_BUFF_SIZE || allLen <= 0) { return NULL;//检查发送数据缓冲区的大小 } int flags = 0; int seqNum = session->seqNum++; unsigned char *buf = (unsigned char *)malloc(allLen);//申请一个buf数组 if (buf == NULL) { return NULL; } memset_s(buf, allLen, 0, allLen);//初始化buf数组 unsigned int magicnum = PKG_HEADER_IDENTIFIER; AesGcmCipherKey cipherKey = { 0}; unsigned char* randomIv = GenerateRandomIv(); if (randomIv == NULL) { free(buf); return NULL; } int offset = 0; int ret = memcpy_s(buf + offset, allLen - offset, &magicnum, SIZE_OF_INT); offset += SIZE_OF_INT; ret += memcpy_s(buf + offset, allLen - offset, &seqNum, SIZE_OF_INT); offset += SIZE_OF_INT; ret += memcpy_s(buf + offset, allLen - offset, &flags, SIZE_OF_INT); offset += SIZE_OF_INT; int dataLen = sendDataLen + OVERHEAD_LEN; ret += memcpy_s(buf + offset, allLen - offset, &dataLen, SIZE_OF_INT); offset += SIZE_OF_INT; //不断将magicnum,seqNum,flags,dataLen中内容复制到指定数组buf的指定位置 cipherKey.keybits = GCM_KEY_BITS_LEN_256; ret += memcpy_s(cipherKey.key, SESSION_KEY_LENGTH, session->sessionKey, SESSION_KEY_LENGTH); ret += memcpy_s(cipherKey.iv, IV_LEN, randomIv, IV_LEN); //将session->sessionKey写进cipherKey.key,将随机向量randomIv写进cipherKey.iv,同时释放randomIv free(randomIv); ret += memcpy_s(cipherKey.iv, SIZE_OF_INT, &seqNum, SIZE_OF_INT); if (ret != 0) { free(buf); return NULL; } int cipherLen = EncryptTransData(&cipherKey, sendData, sendDataLen, buf + offset, sendDataLen + OVERHEAD_LEN);//对数据发送并进行加密,写进buf if (cipherLen <= 0) { SOFTBUS_PRINT("[TRANS] TransPackBytes EncryptTransData fail\n"); free(buf); return NULL; } *bufLen = cipherLen + TRANS_PACKET_HEAD_SIZE; return buf;//将接收数据的buf缓冲区返回 } int SendBytes(int sessionfd, const unsigned char *buf, unsigned int size)//发送数据的函数,第一个为会话fd,buf为要发送的数据,size为发送数据的大小 { if (buf == NULL || sessionfd < 0 || size == 0 || size > SEND_BUFF_MAX_SIZE) { return TRANS_FAILED;//检查参数 } if (SoftBusCheckPermission(SOFTBUS_PERMISSION_NAME) != 0) { return TRANS_FAILED;//检查权限 } TcpSession *session = GetSessionById(sessionfd);//用会话套接字来获取对应会话 if (session == NULL) { return TRANS_FAILED; } int cipherLen = 0; char *cipherBuf = (char *)TransPackBytes(session, buf, size, &cipherLen);//数据传送 if (cipherBuf == NULL) { return TRANS_FAILED; } if (cipherLen <= 0 && cipherBuf != NULL) { free(cipherBuf); return TRANS_FAILED; } int32_t bytes = 0; fd_set writefds; FD_ZERO(&writefds);//初始化一个套接字集合 FD_SET(sessionfd, &writefds);//将sessionfd添加进套接字集合writefds中 struct timeval msTimeout; msTimeout.tv_sec = DEFAULT_TIMEOUT / ONE_SEC; msTimeout.tv_usec = (DEFAULT_TIMEOUT % ONE_SEC) * ONE_SEC; int err = select(sessionfd + 1, NULL, &writefds, NULL, &msTimeout);//监视文件描述符的情况是否可写 if (err <= 0) { SOFTBUS_PRINT("[TRANS] SendBytes select err\n"); free(cipherBuf); //监视失败释放密钥空间 return TRANS_FAILED; } while (FD_ISSET(sessionfd, &writefds) && bytes < (int32_t)cipherLen && (sessionfd) >= 0) { //当sessionfd位于集合writefds中,并且密钥和绘画存在时候 errno = 0; int32_t rc = send(sessionfd, cipherBuf + bytes, cipherLen - bytes, 0); //发送cipherBuf + bytes数据到连接的socket端 if ((rc == -1) && (errno == EAGAIN)) { continue;//发送失败且错误码为 } else if (rc <= 0) { if (bytes == 0) { bytes = -1; } break; } bytes += rc;//发送数据大小计算 } free(cipherBuf); //释放密钥缓冲区 return 0; } 下面代码块中三个函数GetMySessionName,GetPeerSessionName,GetPeerDeviceId实现通过参数获取会话的一些信息。 int GetMySessionName(int sessionId, char *sessionName, unsigned int len)//获取当前会话名 { if (sessionName == NULL) { return TRANS_FAILED;//检查参数 } if (SoftBusCheckPermission(SOFTBUS_PERMISSION_NAME) != 0) { return TRANS_FAILED;//检查用户权限 } TcpSession *session = GetSessionById(sessionId);//用id获取session、 if (session == NULL) { SOFTBUS_PRINT("[TRANS] GetMySessionName GetSessionById fail\n"); return TRANS_FAILED; //检查是否获取成功 } char *name = session->sessionName; if (strlen(name) > len) { SOFTBUS_PRINT("[TRANS] len is too small.\n"); return TRANS_FAILED; } if (strncpy_s(sessionName, len, name, strlen(name)) != 0) { SOFTBUS_PRINT("[TRANS] devId copy failed.\n"); return TRANS_FAILED; //将会话名记入sessionName } return 0; } int GetPeerSessionName(int sessionId, char *sessionName, unsigned int len) //获取同级session的名字 { if (sessionName == NULL) { SOFTBUS_PRINT("[TRANS] sessionName == NULL\n"); return TRANS_FAILED; } if (SoftBusCheckPermission(SOFTBUS_PERMISSION_NAME) != 0) { return TRANS_FAILED;//检查权限 } // Peer session name equal to my session name. return GetMySessionName(sessionId, sessionName, len);//该函数返回sessionid指向会话的名字写进sessionName数组 } int GetPeerDeviceId(int sessionId, char *devId, unsigned int len) { if (devId == NULL) { SOFTBUS_PRINT("[TRANS] devId == NULL\n"); return TRANS_FAILED; //检查传入设备id } if (SoftBusCheckPermission(SOFTBUS_PERMISSION_NAME) != 0) { return TRANS_FAILED; //检查用户权限 } TcpSession *session = GetSessionById(sessionId); //通过id来获取会话 if (session == NULL) { SOFTBUS_PRINT("[TRANS] GetPeerDeviceId GetSessionById fail\n"); return TRANS_FAILED; //检查会话是否为空 } char *id = session->deviceId; if (strlen(id) > len) { SOFTBUS_PRINT("[TRANS] len is too small.\n"); return TRANS_FAILED; //检查len和id长度 } if (strncpy_s(devId, len, id, strlen(id)) != 0) { SOFTBUS_PRINT("[TRANS] devId copy failed.\n"); return TRANS_FAILED; //将id中内容复制到devid中指定位置 } return 0; } CloseSession函数实现会话的关闭 void CloseSession(int sessionId)//关闭会话 { if (SoftBusCheckPermission(SOFTBUS_PERMISSION_NAME) != 0) { return; //检查权限后退出 } if (GetTcpMgrLock() != 0) { return; //获取tcp管理互斥锁后退出 } CloseTransSession(sessionId); if (ReleaseTcpMgrLock() != 0) { return; //释放tcp互斥锁后退出 } return; } [tcp_socket]: https://forum.trustie.net/forums/3397/detail [Link 1]: https://forum.trustie.net/forums/3440/detail [tcp_session]: https://forum.trustie.net/forums/3447/detail [tcp_session_manager.c]: https://forum.trustie.net/forums/3471/detail [tcp_session_manager.c 1]: https://forum.trustie.net/forums/3477/detail
还没有评论,来说两句吧...