前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swoole 源码分析之 TCP Server 模块

Swoole 源码分析之 TCP Server 模块

原创
作者头像
码农先森
发布2024-06-23 11:23:51
940
发布2024-06-23 11:23:51
举报
文章被收录于专栏:Swoole源码分析

大家好,我是码农先森。

今天我们来分析 TCP Server 模块 的实现原理,下面这张图是来自 Swoole 的官网。

那么,我们就主要分析这段言简意赅的代码,从这段代码中可以看出设置了四个回调方法,还有构造方法 new Swoole\Server(127.0.0.1, 9503)、服务启动方法 $server->start()。别小看上面的那段代码,其中可是蕴含了巨大的能量。我们可以先通过下面这张图,来了解接下来要介绍的内容。

话不多说,开整!

Swoole\Server 类及函数的注册

我们之所以可以使用 new Swoole\Server 类,是需要先将该类注册到 PHP 中,然后才能进行调用。从下面 php_swoole_server_minit 这个方法可以看出类的注册,这个方法里面只是展示了 Swoole\Server 类的注册,其他的省略掉了,比如:Swoole\Server\Task 等,更全的代码可以翻看源文件,下面这张图介绍了类及方法的注册定义。

代码语言:php
复制
// swoole-src/ext-src/swoole_server.cc:454
void php_swoole_server_minit(int module_number) {
    // ---------------------------------------Server-------------------------------------
    // 注册一个名为 "Swoole\Server" 的 PHP 类。swoole_server 是在扩展中定义的 zend_class_entry 结构体的变量名
    // "Swoole\Server" 是该类的名称,nullptr 表示基类(如果有的话),swoole_server_methods 是一个包含类方法的结构体
    SW_INIT_CLASS_ENTRY(swoole_server, "Swoole\\Server", nullptr, swoole_server_methods);

    // Server 类不能被序列化
    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server);

    // Server 类不能被克隆
    SW_SET_CLASS_CLONEABLE(swoole_server, sw_zend_class_clone_deny);

    // Server 类不允许删除属性,并且禁止使用 unset() 函数删除该类的对象的属性
    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server, sw_zend_class_unset_property_deny);

    // 使用自定义对象处理。其中 server_create_object 和 server_free_object 是自定义对象的创建和释放函数
    // ServerObject 是自定义对象的类型,std 是一个宏定义,用于指定对象的父类
    SW_SET_CLASS_CUSTOM_OBJECT(swoole_server, server_create_object, server_free_object, ServerObject, std);
    
    ...
}

在第一行代码中,有这样一个参数 swoole_server_methods,它是一个包含类方法的结构体,我们跟踪过去看看。

代码语言:php
复制
// swoole-src/ext-src/swoole_server.cc:373
static zend_function_entry swoole_server_methods[] = {
    // 对应的代码 $server = new Swoole\Server('127.0.0.1', 9503);
    PHP_ME(swoole_server, __construct, arginfo_class_Swoole_Server___construct, ZEND_ACC_PUBLIC)
    // 对应的代码 $server->on('', function ($server) {})
    PHP_ME(swoole_server, on, arginfo_class_Swoole_Server_on, ZEND_ACC_PUBLIC)
    // 对应的代码 $server->start()
    PHP_ME(swoole_server, start, arginfo_class_Swoole_Server_start, ZEND_ACC_PUBLIC)

    ...
}

这下可以发现 __construct()on()start() 三个方法,即在前面使用到的方法,就是在这里被定义声明的。

Server 类方法的具体实现

先看 __construct 构造方法的具体实现。首先会先判断 Server 类是否已经被实例化过了,如果是则会直接报错,即不能被重复实例化。然后,做一些参数解析、环境检测、运行模式的判断。最后,会将端口号添加是 Server 对象中,如果没有设置端口,系统则会进行自动分配。

代码语言:php
复制
// file: swoole-src/ext-src/swoole_server.cc:1829
static PHP_METHOD(swoole_server, __construct) {
    // 判断是否已经存在一个 Server 对象的实例,如果 serv 不为空,则说明构造函数已经被调用过
    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));
    Server *serv = server_object->serv;
    if (serv) {
        zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));
        RETURN_FALSE;
    }

    // 声明一些变量
    zval *zserv = ZEND_THIS;
    char *host;
    size_t host_len = 0;
    zend_long sock_type = SW_SOCK_TCP;
    zend_long serv_port = 0;
    zend_long serv_mode = Server::MODE_BASE;

    // swoole 只允许在命令行模式下运行
    if (!SWOOLE_G(cli)) {
        zend_throw_exception_ex(
            swoole_exception_ce, -1, "%s can only be used in CLI mode", SW_Z_OBJCE_NAME_VAL_P(zserv));
        RETURN_FALSE;
    }
    
    // 检查 Server 是否已经在运行了
    if (sw_server() != nullptr) {
        zend_throw_exception_ex(
            swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv));
        RETURN_FALSE;
    }

    // 解析构造函数上的参数,例如:127.0.0.1、9503 等
    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4)
    Z_PARAM_STRING(host, host_len)
    Z_PARAM_OPTIONAL
    Z_PARAM_LONG(serv_port)
    Z_PARAM_LONG(serv_mode)
    Z_PARAM_LONG(sock_type)
    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

    // Swoole 只支持 base 和 process 两种模式
    if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS) {
        zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode);
        RETURN_FALSE;
    }
    
    ...
    
    // 在 base 模式下,只会设置一个 reactor 主线程及一个 worker 进程 
    if (serv_mode == Server::MODE_BASE) {
        serv->reactor_num = 1;
        serv->worker_num = 1;
    }

    /* primary port */
    do {
        // 如果没有设置端口号,系统则会随机分配一个端口号
        if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) {
            if (serv->add_systemd_socket() <= 0) {
                zend_throw_error(NULL, "failed to add systemd socket");
                RETURN_FALSE;
            }
        } else {
            // 将 host、port 参数 添加到 server 对象中
            ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port);
            if (!port) {
                zend_throw_exception_ex(swoole_exception_ce,
                                        swoole_get_last_error(),
                                        "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]",
                                        host,
                                        serv_port,
                                        swoole_strerror(swoole_get_last_error()),
                                        swoole_get_last_error());
                RETURN_FALSE;
            }
        }

        // 存在监听多个端口的情况
        for (auto ls : serv->ports) {
            php_swoole_server_add_port(server_object, ls);
        }

        // 设置主端口
        server_object->property->primary_port = (ServerPortProperty *) serv->get_primary_port()->ptr;
    } while (0);

    ...
}

刚刚将 Server 进行了实例化,且设置了相关的参数。接下来再看 $server->on() 回调函数的设置。

代码语言:php
复制
// file: swoole-src/ext-src/swoole_server.cc:2343
static PHP_METHOD(swoole_server, on) {
    // 检查 Server 是否已经运行
    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);
    if (serv->is_started()) {
        php_swoole_fatal_error(E_WARNING, "server is running, unable to register event callback function");
        RETURN_FALSE;
    }

    // 事件名称,例如:start
    zval *name;
    // 回调函数 ,例如:function($server){}
    zval *cb;

    // 参数解析
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &name, &cb) == FAILURE) {
        RETURN_FALSE;
    }

    // 主要是检查回调函数是否能够被正常的调用
    char *func_name = nullptr;
    zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache));
    if (!sw_zend_is_callable_ex(cb, nullptr, 0, &func_name, nullptr, fci_cache, nullptr)) {
        php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name);
        return;
    }
    efree(func_name);

    zend::String _event_name_ori(name);
    zend::String _event_name_tolower(zend_string_tolower(_event_name_ori.get()), false);

    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));

    // 寻找对应的事件类型,例如: start、connect 等
    auto i = server_event_map.find(_event_name_tolower.to_std_string());
    // 没有找到的情况,i == server_event_map.end() 这个条件没有匹配的事件找到才会成立
    if (i == server_event_map.end()) {
        zval *port_object = server_object->property->ports.at(0);
        zval retval;
        efree(fci_cache);
        sw_zend_call_method_with_2_params(port_object, swoole_server_port_ce, nullptr, "on", &retval, name, cb);
        RETURN_BOOL(Z_BVAL_P(&retval));
    } else {
        // 找到的情况
        int event_type = i->second.type;
        // 拼接属性名称 例如: onstart
        std::string property_name = "on" + i->second.name; 

        // 将回调函数更新到 Server 对象中
        zend_update_property(
            swoole_server_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb);

        if (server_object->property->callbacks[event_type]) {
            efree(server_object->property->callbacks[event_type]);
        }
        server_object->property->callbacks[event_type] = fci_cache;

        RETURN_TRUE;
    }
}

设置好回调函数后,就开始调用 $server->start() 这意味这正式启动服务了。

代码语言:php
复制
// file: swoole-src/ext-src/swoole_server.cc:2548
static PHP_METHOD(swoole_server, start) {
    zval *zserv = ZEND_THIS;
    Server *serv = php_swoole_server_get_and_check_server(zserv);

    // 检查 Server 是否已经启动
    if (serv->is_started()) {
        php_swoole_fatal_error(
            E_WARNING, "server is running, unable to execute %s->start()", SW_Z_OBJCE_NAME_VAL_P(zserv));
        RETURN_FALSE;
    }
    
    // 检查 Server 是否已经关闭
    if (serv->is_shutdown()) {
        php_swoole_fatal_error(
            E_WARNING, "server have been shutdown, unable to execute %s->start()", SW_Z_OBJCE_NAME_VAL_P(zserv));
        RETURN_FALSE;
    }

    // 检查 事件循环 是否已经创建
    if (SwooleTG.reactor) {
        php_swoole_fatal_error(
            E_WARNING, "eventLoop has already been created, unable to start %s", SW_Z_OBJCE_NAME_VAL_P(zserv));
        RETURN_FALSE;
    }

    ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2));
    // 注册相关的回调函数
    server_object->register_callback();
    // 做一些前置操作
    server_object->on_before_start();

    // 设置运行模式、启动守护进程、设置共享内存,最后启动底层的事件驱动循环
    if (serv->start() < 0) {
        php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error);
    }

    RETURN_TRUE;
}

回调函数的实现

这里我主要介绍上面所提到的四个回调函数的实现,start 回调函数对应的实现函数是 php_swoole_server_onStart(Server *serv)、connect 回调函数对应的实现函数是php_swoole_server_onConnect(Server *serv, DataHead *info)、receive 回调函数对应的实现函数是 php_swoole_server_onReceive(Server *serv, RecvData *req)、close 回调函数对应的实现函数是 php_swoole_server_onClose(Server *serv, DataHead *info)。这些函数的实现中都会使用到 Zend 引擎所提供的 zend::function::call() 函数,用于触发用户自定义的回调函数及参数的回传。可以先看这张图,也是简要的介绍了四个回调函数的实现逻辑,让大家先有个基本的印象。

php_swoole_server_onStart

  1. 使用 SW_SERVER_CB_onStart 常量通过在 callbacks 数组中获取到 onStart 回调函数的数据。
  2. 还会更新 Server 对象的 master_pidmanager_pid
  3. 如果启用了 library 模块,则会调用一下对应的方法,这里其实也类似于 Hook 的形式。
  4. 最后判断是否有注册 onStart 回调函数,如果有则调用该回调函数,并且将 zserv Server 对象传递给回调函数。
  5. 至此就是会执行到我们用户写的业务逻辑了。
  6. 这个 onStart 回调函数,当我们的服务一启动便会执行,可以在这里做一些初始化的操作。
代码语言:php
复制
// file: swoole-src/ext-src/swoole_server.cc:1371
static void php_swoole_server_onStart(Server *serv) {
    serv->lock();
    zval *zserv = (zval *) serv->private_data_2;
    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));
    // 从 callbacks 数组中获取 onStart 回调函数的信息
    auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onStart];

    // 更新服务对象的 master_pid 和 manager_pid 属性,分别表示主进程和管理进程的 PID。
    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid);
    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid);

    // 如果启用了 library 模块,则会调用对应的方法。具体的方法实现可查阅 https://github.com/swoole/library/blob/master/src/core/Server/Helper.php
    if (SWOOLE_G(enable_library)) {
        zend::function::call("\\Swoole\\Server\\Helper::onStart", 1, zserv);
    }

    // 判断是否有注册 onStart 回调函数,并调用该回调函数
    if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, serv->is_enable_coroutine()))) {
        php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv));
    }
    serv->unlock();
}

php_swoole_server_onConnect

  1. 使用 SW_SERVER_CB_onConnect 常量通过 php_swoole_server_get_fci_cache 函数获取 onConnect 回调函数数据。
  2. 如果启用了事件对象 'event_object' => true,回调函数的参数,会集成到事件对象中,例如:function (Swoole\Server $serv, Swoole\Server\Event $object){}
  3. 判断是否有注册 onConnect 回调函数,并调用该回调函数。
  4. 其中的 args 是回调的参数,参数有 $server$fd$reactor_id
  5. 不过 $reactor_id 这个参数,只有在多进程模式下才会有值。
  6. 这个 onConnect 回调函数,当有用户连接之后会进行回调该函数,我们可以在这个函数里处理连接数据。
代码语言:php
复制
// file: swoole-src/ext-src/swoole_server.cc:1604
void php_swoole_server_onConnect(Server *serv, DataHead *info) {
    // 获取 onConnect 回调函数的信息
    auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onConnect);
    if (!fci_cache) {
        return;
    }

    zval *zserv = (zval *) serv->private_data_2;
    zval args[3];
    int argc;
    args[0] = *zserv;

    // 如果启用了事件对象 'event_object' => true
    // 回调函数的参数,会集成到事件对象中,例如:function (Swoole\Server $serv, Swoole\Server\Event $object){}
    if (serv->event_object) {
        zval *object = &args[1];
        object_init_ex(object, swoole_server_event_ce);
        zend_update_property_long(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("fd"), (zend_long) info->fd);
        zend_update_property_long(
            swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("reactor_id"), (zend_long) info->reactor_id);
        zend_update_property_double(
            swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), info->time);
        argc = 2;
    } else {
        ZVAL_LONG(&args[1], info->fd);
        ZVAL_LONG(&args[2], info->reactor_id);
        argc = 3;
    }

    // 判断是否有注册 onConnect 回调函数,并调用该回调函数
    // 其中的 args 是回调的参数,例如:$server、$fd
    if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) {
        php_swoole_error(E_WARNING, "%s->onConnect handler error", SW_Z_OBJCE_NAME_VAL_P(zserv));
    }

    if (serv->event_object) {
        zval_ptr_dtor(&args[1]);
    }
}

php_swoole_server_onReceive

  1. 使用 SW_SERVER_CB_onReceive 常量通过 php_swoole_server_get_fci_cache 函数获取 onReceive 回调函数数据。
  2. 如果启用了事件对象 'event_object' => true,回调函数的参数,会集成到事件对象中,例如:function (Swoole\Server $serv, Swoole\Server\Event $object){}
  3. 判断是否有注册 onReceive 回调函数,并调用该回调函数。
  4. 其中的 args 是回调的参数,参数有 $server$fd$reactor_id$data
  5. 不过 $reactor_id 这个参数,只有在多进程模式下才会有值。
  6. 相较于 onConnect 回调函数,这里多了一个 $data 参数,这个参数就是用户发送的数据。
  7. 我们接收到数据,那么就可以进行业务逻辑的处理了,处理完后,要及时响应。
  8. 通常情况下,为了提高服务的处理能力,会使用到协程来分发请求。
代码语言:php
复制
// file: swoole-src/ext-src/swoole_server.cc:1076
int php_swoole_server_onReceive(Server *serv, RecvData *req) {
    // 获取 onReceive 回调函数的信息
    auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive);

    if (fci_cache) {
        zval *zserv = (zval *) serv->private_data_2;
        zval args[4];
        int argc;

        args[0] = *zserv;
        
        // 如果启用了事件对象 'event_object' => true
        // 回调函数的参数,会集成到事件对象中,例如:function (Swoole\Server $serv, Swoole\Server\Event $object){}
        if (serv->event_object) {
            zval *object = &args[1];
            zval data;
            object_init_ex(object, swoole_server_event_ce);
            zend_update_property_long(
                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("fd"), (zend_long) req->info.fd);
            zend_update_property_long(
                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("reactor_id"), (zend_long) req->info.reactor_id);
            zend_update_property_double(
                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time);
            php_swoole_get_recv_data(serv, &data, req);
            zend_update_property(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), &data);
            zval_ptr_dtor(&data);
            argc = 2;
        } else {
            // 对参数 args 进行赋值
            ZVAL_LONG(&args[1], (zend_long) req->info.fd);
            ZVAL_LONG(&args[2], (zend_long) req->info.reactor_id);
            php_swoole_get_recv_data(serv, &args[3], req);
            argc = 4;
        }
      
        // 判断是否有注册 onReceive 回调函数,并调用该回调函数
        // 其中的 args 是回调的参数,例如:$server, $fd, $reactor_id, $data
        if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) {
            php_swoole_error(E_WARNING, "%s->onReceive handler error", SW_Z_OBJCE_NAME_VAL_P(zserv));
            serv->close(req->info.fd, false);
        }
        if (serv->event_object) {
            zval_ptr_dtor(&args[1]);
        } else {
            // 释放内存资源
            zval_ptr_dtor(&args[3]);
        }
    }

    return SW_OK;
}

php_swoole_server_onClose

  1. 关闭服务之前,要先清除的相关协程资源,避免资源浪费。
  2. 使用 SW_SERVER_CB_onClose 常量通过 php_swoole_server_get_fci_cache 方法获取到 onClose 回调函数信息。
  3. 如果是 Webscoket 连接,则获取对应的回调函数 onDisconnect 信息。
  4. 如果启用了事件对象 'event_object' => true,回调函数的参数,会集成到事件对象中,例如:function (Swoole\Server $serv, Swoole\Server\Event $object){}
  5. 判断是否有注册 onClose 回调函数,并调用该回调函数。
  6. 其中的 args 是回调的参数,参数包括:$server, $fd, $reactor_id
  7. 回调到用户的函数中,做一些业务上的收尾处理,至此服务就结束了。
代码语言:php
复制
// file: swoole-src/ext-src/swoole_server.cc:1639
void php_swoole_server_onClose(Server *serv, DataHead *info) {
    zval *zserv = (zval *) serv->private_data_2;
    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));
    SessionId session_id = info->fd;

    // 如果启用了协程,则需要清除这个连接的相关协程资源
    if (serv->enable_coroutine && serv->send_yield) {
        auto _i_co_list = server_object->property->send_coroutine_map.find(session_id);
        if (_i_co_list != server_object->property->send_coroutine_map.end()) {
            auto co_list = _i_co_list->second;
            server_object->property->send_coroutine_map.erase(session_id);
            while (!co_list->empty()) {
                Coroutine *co = co_list->front();
                co_list->pop_front();
                swoole_set_last_error(ECONNRESET);
                co->resume();
            }
            delete co_list;
        }
    }

    // 获取 onClose 回调信息
    auto *fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onClose);
    Connection *conn = serv->get_connection_by_session_id(session_id);
    if (!conn) {
        return;
    }

    // 如果是 Webscoket 连接,则获取对应的回调函数 onDisconnect 信息
    if (conn->websocket_status != swoole::websocket::STATUS_ACTIVE) {
        ListenPort *port = serv->get_port_by_server_fd(info->server_fd);
        if (port && port->open_websocket_protocol &&
            php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onDisconnect)) {
            fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onDisconnect);
        }
    }
    if (fci_cache) {
        zval *zserv = (zval *) serv->private_data_2;
        zval args[3];
        int argc;
        args[0] = *zserv;

        // 如果启用了事件对象 'event_object' => true
        // 回调函数的参数,会集成到事件对象中,例如:function (Swoole\Server $serv, Swoole\Server\Event $object){}
        if (serv->event_object) {
            zval *object = &args[1];
            object_init_ex(object, swoole_server_event_ce);
            zend_update_property_long(
                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("fd"), (zend_long) session_id);
            zend_update_property_long(
                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("reactor_id"), (zend_long) info->reactor_id);
            zend_update_property_double(
                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), info->time);
            argc = 2;
        } else {
            // 对参数 args 进行赋值
            ZVAL_LONG(&args[1], session_id);
            ZVAL_LONG(&args[2], info->reactor_id);
            argc = 3;
        }

        // 判断是否有注册 onClose 回调函数,并调用该回调函数
        // 其中的 args 是回调的参数,例如:$server, $fd, $reactor_id
        if (UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, serv->enable_coroutine))) {
            php_swoole_error(E_WARNING, "%s->onClose handler error", SW_Z_OBJCE_NAME_VAL_P(zserv));
        }

        // 释放资源
        if (serv->event_object) {
            zval_ptr_dtor(&args[1]);
        }
    }

    // 释放 HTTP2 流资源
    if (conn->http2_stream) {
        swoole_http2_server_session_free(conn);
    }
}

总结

从 Swoole 官网的这段短小精悍的代码,就可以看出 Server 服务的关键要点。再通过对类、构造方法、回调函数的层层剖析,我们逐渐的了解了底层的实现原理。TCP Server 模式是基础模块,我们经常使用的 HTTP 模块就是基于此模块进行扩展的,所以我们需要有一定的了解。对我们来说掌握了原理性的内容,在回过头去看用户级的代码,往往会更轻松,所以我们需要有耐心的琢磨。

欢迎关注、分享、点赞、收藏、在看,我是微信公众号「码农先森」作者。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Swoole\Server 类及函数的注册
  • Server 类方法的具体实现
  • 回调函数的实现
    • php_swoole_server_onStart
      • php_swoole_server_onConnect
        • php_swoole_server_onReceive
          • php_swoole_server_onClose
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档