草庐IT

OpenHarmony源码分析之分布式软总线:trans_service模块(1)/认证通道管理

xjingxuan 2023-04-12 原文

一、 概述

trans_service模块基于系统内核提供的socket通信,向authmanager模块提供设备认证通道管理和设备认证数据的传输;向业务模块提供session管理和基于session的数据收发功能,并且通过GCM模块的加密功能提供收发报文的加解密保护。

二、 源码分析

  1. 入口函数为StartListener(),主要是针对Linux系统内核,其它系统之后会有补充,解析如下
/*
函数功能: 启动监听其他设备的连接请求或者新数据响应
函数参数:callback  回调函数的地址;ip  需要绑定的server端ip地址
函数返回值:返回建立TCP连接的套接字的port
详细:监听器的主要作用是监听新连接请求和已建立连接的数据通信。
    1.首先作为server端产生监听套接字fd,然后绑定端口和IP,这里的端口绑定采用了动态分配的方式。
    2.然后创建子线程执行监听任务,采用的是select的io复用方式来处理并发请求。
*/
int StartListener(BaseListener *callback, const char *ip)
{
    if (callback == NULL || ip == NULL) {
        return -DBE_BAD_PARAM;
    }

    g_callback = callback;
	//产生并初始化监听描述符,传入的SESSIONPORT为0,绑定0端口号的意义在于动态选取本地可用端口号,不用考虑端口重用的问题
    int rc = InitListenFd(ip, SESSIONPORT);
    if (rc != DBE_SUCCESS) {
        return -DBE_BAD_PARAM;
    }
	//捕获SIGPIPE信号,SIG_IGN表示忽视该信号,不执行SIGPIPE默认操作:终止程序
    signal(SIGPIPE, SIG_IGN);
    //初始化线程属性,"auth"为线程名,线程栈大小为0x800 | MIN_STACK_SIZE,单位为字节,线程优先级为20
    ThreadAttr attr = {"auth", 0x800, 20, 0, 0};
    //创建子线程进行监听任务,包括设备认证过程中的新连接或者新数据
    register ThreadId threadId = (ThreadId)AuthCreate((Runnable)WaitProcess, &attr);
    if (threadId == NULL) {//创建子线程失败,则返回-1
        SOFTBUS_PRINT("[TRANS] StartListener AuthCreate fail\n");
        return -1;
    }
    //返回建立TCP连接的套接字的port,因为这里绑定的端口是0,其实是内核随机分配的端口号
    return GetSockPort(g_listenFd);
}
  1. 调用 InitListenFd() 函数产生并初始化监听套接字fd
/*
函数功能: 产生并初始化监听描述符
函数参数:ip    server端IP地址;port    server端端口
函数返回值:成功返回0,失败返回错误码
详细:
*/
static int InitListenFd(const char *ip, int port)//
{
    if (ip == NULL || g_listenFd != -1) {
        return -DBE_BAD_PARAM;
    }

    if (strncmp(ip, "0.0.0.0", strlen(ip)) == 0) {//如果输入ip为0.0.0.0,则返回错误码
        return -DBE_BAD_PARAM;
    }

    int rc = OpenTcpServer(ip, port);//初始化server端套接字,生成监听fd
    if (rc < 0) {
        SOFTBUS_PRINT("[TRANS] InitListenFd OpenTcpServer fail\n");
        return rc;
    }
    g_listenFd = rc;
    //更新最大描述符值,因为后续需要为select函数提供最大描述符值,select函数需要获得监听集合中所有文件描述符的范围
    RefreshMaxFd(g_listenFd);
    //将该fd从CLOSED转换到LISTEN状态,监听client端(主动端)发起的连接信息
    rc = listen(rc, DEFAULT_BACKLOG);
    if (rc != 0) {
        SOFTBUS_PRINT("[TRANS] InitListenFd listen fail\n");
        StopListener();
        return -DBE_LISTEN_FAIL;
    }

    return DBE_SUCCESS;
}
  1. OpenTcpServer() 函数
/*
函数功能: 初始化server端套接字,绑定ip地址及port
函数参数:ip    需要进行绑定的ip地址;port  需要进行绑定的port
函数返回值:成功返回生成的套接字描述符,失败返回错误码
详细:
*/
int OpenTcpServer(const char *ip, uint16_t port)
{
    if (ip == NULL) {
        return -DBE_BAD_PARAM;
    }

    struct sockaddr_in addr = {0};
    errno = 0;
    //将点分十进制的ip字符串转化为网络字节序的32位ip地址
    int rc = inet_pton(AF_INET, ip, &addr.sin_addr);
    if (rc <= 0) {
        return -DBE_BAD_IP;
    }

    addr.sin_family = AF_INET;//ipv4
    addr.sin_port = htons(port);//网络字节序的port

    errno = 0;
    int fd = socket(AF_INET, SOCK_STREAM, 0);//生成基于TCP协议的套接字描述符
    if (fd < 0) {
        return -DBE_OPEN_SOCKET;
    }

    SetServerOption(fd);//设置套接字选项,地址可重用和禁用Nagle算法
    errno = 0;
    rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));//绑定server端IP地址和port
    if (rc < 0) {
        ShutDown(fd);
        return -DBE_BIND_SOCKET;
    }

    return fd;
}
  1. 创建线程用于设备身份认证:AuthCreate() 函数
/*
函数功能: 初始化用于身份验证的线程相关属性并创建线程
函数参数:run   子线程回调函数;attr    线程属性结构体地址
函数返回值:线程id
详细:
*/
ThreadId AuthCreate(Runnable run, const ThreadAttr *attr)
{
    pthread_attr_t threadAttr;
    pthread_attr_init(&threadAttr);//初始化一个线程属性对象threadAttr
    pthread_attr_setstacksize(&threadAttr, (attr->stackSize | MIN_STACK_SIZE));//设置线程的栈大小
    struct sched_param sched = {attr->priority};
    pthread_attr_setschedparam(&threadAttr, &sched);//设置线程调度优先级
    pthread_key_create(&g_localKey, NULL);//创建线程私有的全局数据(TSD),同名而地址不同
    pthread_t threadId = 0;
    int errCode = pthread_create(&threadId, &threadAttr, run, NULL);//创建线程
    if (errCode != 0) {
        return NULL;
    }
    return (ThreadId)threadId;//返回void * 类型的线程id
}
  1. 执行线程回调函数 WaitProcess()
/*
函数功能: 使用忙等方式,调用select()来监听listenFd和数据g_dataFd的信息,如果监听到有数据可读,则进入ProcessAuthData来处理
函数参数:无
函数返回值:无
详细:
*/
static void WaitProcess(void)
{
    SOFTBUS_PRINT("[TRANS] WaitProcess begin\n");
    fd_set readSet;//可读描述符fd集合
    fd_set exceptfds;//异常描述符fd集合

    while (1) {//轮询监听可读事件或者异常事件的变化
        /*每次调用select前都要重新设置文件描述符和时间,因为事件发生后,文件描述符和时间都被内核修改*/
        FD_ZERO(&readSet);//清空描述符集合
        FD_ZERO(&exceptfds);
        FD_SET(g_listenFd, &readSet);//将监听fd添加到可读集合中
        if (g_dataFd >= 0) {//如果通信描述符存在,将通信描述符添加至可读集合和异常集合中
            FD_SET(g_dataFd, &readSet);
            FD_SET(g_dataFd, &exceptfds);
        }
        //开始监听readSet和exceptfds
        int ret = select(g_maxFd + 1, &readSet, NULL, &exceptfds, NULL);
        if (ret > 0) {//ret大于0表示监听的集合中有ret个描述符状态发生改变
            if (!ProcessAuthData(g_listenFd, &readSet)) {//处理接收到的认证数据
                SOFTBUS_PRINT("[TRANS] WaitProcess ProcessAuthData fail\n");
                StopListener();//关闭监听器
                break;
            }
        } else if (ret < 0) {//发生错误
        	//产生中断或者异常
            if (errno == EINTR || (g_dataFd > 0 && FD_ISSET(g_dataFd, &exceptfds))) {
                SOFTBUS_PRINT("[TRANS] errno == EINTR or g_dataFd is in exceptfds set.\n");
                CloseAuthSessionFd(g_dataFd);//关闭认证会话的连接
                continue;
            }
            SOFTBUS_PRINT("[TRANS] WaitProcess select fail, stop listener\n");
            StopListener();//关闭监听器
            break;
        }
    }
}
  1. ProcessAuthData() 函数
/*
函数功能: 处理listenFd的事件调用accept建立连接生成通信描述符g_dataFd,或者处理g_dataFd的通信事件
函数参数:监听描述符listenFd,可读描述符集合
函数返回值:成功返回true,失败返回false
详细:
*/
static bool ProcessAuthData(int listenFd, const fd_set *readSet)
{
    if (readSet == NULL || g_callback == NULL || g_callback->onConnectEvent == NULL ||
        g_callback->onDataEvent == NULL) {
        return false;
    }
	//如果有设备发起连接,响应listenFd事件,accept建立socket连接,然后调用回调函数onConnectEvent处理连接事件
    if (FD_ISSET(listenFd, readSet)) {
        struct sockaddr_in addrClient = {0};
        socklen_t addrLen = sizeof(addrClient);

        g_dataFd = accept(listenFd, (struct sockaddr *)(&addrClient), &addrLen);//建立socket连接
        if (g_dataFd < 0) {//accept函数执行出错
            CloseAuthSessionFd(listenFd);
            return false;
        }
        //更新最大描述符值,因为后续需要为select函数提供最大描述符值,select函数需要获得监听集合中所有文件描述符的范围
        RefreshMaxFd(g_dataFd);
        if (g_callback->onConnectEvent(g_dataFd, inet_ntoa(addrClient.sin_addr)) != 0) {//响应连接事件
            CloseAuthSessionFd(g_dataFd);
        }
    }
	//如果接收到设备发送的可读数据,响应g_dataFd的事件,调用回调函数onDataEvent处理数据传输事件
    if (g_dataFd > 0 && FD_ISSET(g_dataFd, readSet)) {
        g_callback->onDataEvent(g_dataFd);//响应通信事件
    }

    return true;
}

至此,trans_service模块为认证模块提供了认证通道管理和认证数据的收发,具体的认证过程及具体的认证协议在authmanager模块实现。

有关OpenHarmony源码分析之分布式软总线:trans_service模块(1)/认证通道管理的更多相关文章

  1. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  2. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  3. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  4. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  5. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  6. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  7. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  8. ruby - 分布式事务和队列,ruby,erlang,scala - 2

    我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和

  9. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  10. ruby-on-rails - Controller 中的 Rails 辅助模块 - 2

    我有一个Controller,我想为这个Controller创建一个助手,我可以在不包含它的情况下使用它。我尝试像这样创建一个与Controller同名的助手classCars::EnginesController我创建的助手是moduleCars::EnginesHelperdefcheck_fuellogger.debug("chekingfuel")endend我得到的错误是undefinedlocalvariableormethod`check_fuel'for#有没有我遗漏的约定? 最佳答案 如果你真的想在Controll

随机推荐