TCP服务器实现-swoole回调函数注册过程

版权声明:转载请注明来源 https://blog.csdn.net/u013702678/article/details/81160288

接着上一篇继续分析,上次分析到swoole_server的创建完成,对着我们之前的demo,现在到swoole_server的回调函数的注册流程了。

//监听连接进入事件
$serv->on('connect', function ($serv, $fd) {  
    echo "Client: Connect.\n";
});

//监听数据接收事件
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
    $serv->send($fd, "Server: ".$data);
});

//监听连接关闭事件
$serv->on('close', function ($serv, $fd) {
    echo "Client: Close.\n";
});

这几个注册函数对到swoole底层实现,其实是同一个,即swoole_server的on函数,只是传入参数不同。这三个回调函数的参数也是类似,有一个动作名和回调函数实现,下面我们进入代码看实现,这里基本上都是PHP扩展相关的实现,有些不是很清楚,后面再来完善下。

PHP_METHOD(swoole_server, on)
{
    zval *name;//变量,后续用于存储PHP侧传入的动作名
    zval *cb;//变量,后续用于存储PHP侧传入的回调函数指针信息

    //如上一篇提到的,在new swoole_server后,swoole会缓存该对象,这里直接从缓存读取
    swServer *serv = swoole_get_object(getThis());
    //swoole_server已经启动,抛异常,停止程序,serv->gs->start是在swoole_server的start方法调用后设置值的
    if (serv->gs->start > 0)
    {
        swoole_php_fatal_error(E_WARNING, "server is running. unable to register event callback function.");
        RETURN_FALSE;
    }
 
    //PHP扩展API,用于获取PHP侧的输入,输入的动作名和回调函数分别放在name和cb中
    if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "zz", &name, &cb) == FAILURE)
    {
        return;
    }
    //PHP Zend的API,用于判断cb是否可以被调用,类似PHP里面的is_callable函数,如果不可以调用,则打印日志,中断流程,这个函数同时会把这个回调函数的输入参数和实现分别通过func_name和func_cache回写回来,func_cache用于后续缓存函数
    char *func_name = NULL;
    zend_fcall_info_cache *func_cache = emalloc(sizeof(zend_fcall_info_cache));
    if (!sw_zend_is_callable_ex(cb, NULL, 0, &func_name, NULL, func_cache, NULL TSRMLS_CC))
    {
        swoole_php_fatal_error(E_ERROR, "function '%s' is not callable", func_name);
        efree(func_name);//释放空间
        return;
    }
    //释放变量值
    efree(func_name);
    //这个函数的实现没有找到,但是预测是将char*类型的变量转换为PHP内部的zval变量
    convert_to_string(name);
    //定义所有的回调动作名,swoole_server可以注册的动作必须得在里面
    char *callback_name[PHP_SERVER_CALLBACK_NUM] = {
        "Connect",
        "Receive",
        "Close",
        "Packet",
        "Start",
        "Shutdown",
        "WorkerStart",
        "WorkerStop",
        "Task",
        "Finish",
        "WorkerExit",
        "WorkerError",
        "ManagerStart",
        "ManagerStop",
        "PipeMessage",
        NULL,
        NULL,
        NULL,
        NULL,
        "BufferFull",
        "BufferEmpty",
    };
    
    int i;
    char property_name[128];//定义完整的函数名地址信息,完整的函数名类型onReceive
    int l_property_name = 0;
    memcpy(property_name, "on", 2);//这里先把on两个字符拷贝到property_name中
    //遍历所有的动作名信息
    for (i = 0; i < PHP_SERVER_CALLBACK_NUM; i++)
    {
        if (callback_name[i] == NULL)
        {
            continue;
        }
        //如果遍历过程中,找到和PHP侧传入的动作名相等的,则将on***的函数更新到swoole_server对象上,类似如果PHP侧传入的动作名是receive,会将onreceive注册到swoole_server对象上,当然这里还有想要函数的实现也会带过去。
        if (strncasecmp(callback_name[i], Z_STRVAL_P(name), Z_STRLEN_P(name)) == 0)
        {
            memcpy(property_name + 2, callback_name[i], Z_STRLEN_P(name));//拷贝name信息
            l_property_name = Z_STRLEN_P(name) + 2;
            property_name[l_property_name] = '\0';//C语言字符串
            zend_update_property(swoole_server_class_entry_ptr, getThis(), property_name, l_property_name, cb TSRMLS_CC);//更新到对象上去。
            php_sw_server_callbacks[i] = sw_zend_read_property(swoole_server_class_entry_ptr, getThis(), property_name, l_property_name, 0 TSRMLS_CC);//缓存回调函数实现
            php_sw_server_caches[i] = func_cache;//缓存函数实现
            sw_copy_to_stack(php_sw_server_callbacks[i], _php_sw_server_callbacks[i]);
            break;
        }
    }
    //如果PHP侧输入的动作名没在swoole允许的注册名内,则抛错,中断程序
    if (l_property_name == 0)
    {
        swoole_php_error(E_WARNING, "unknown event types[%s]", Z_STRVAL_P(name));
        efree(func_cache);
        RETURN_FALSE;
    }
    
    //这里如果是小于SW_SERVER_CB_onStart,也就是用户注册的动作为swoole_server执行start之前的动作,则将动作也注册到port_object对象上,这里后面再看哪里有用到。
    if (i < SW_SERVER_CB_onStart)
    {
        zval *port_object = server_port_list.zobjects[0];
        zval *retval = NULL;
        sw_zval_add_ref(&port_object);
        sw_zend_call_method_with_2_params(&port_object, swoole_server_port_class_entry_ptr, NULL, "on", &retval, name, cb);
    }
    else
    {
        RETURN_TRUE;
    }
}

猜你喜欢

转载自blog.csdn.net/u013702678/article/details/81160288