使用GSocketService创建Socket服务详解

GSocketService 是 GLib/GIO 库中的一个核心类,用于简化异步网络服务或本地套接字服务的创建。它工作在 GLib 的主事件循环上,能高效地处理并发连接。

🧩 GSocketService 核心解读

你可以通过下面的表格快速了解它的关键特性:

特性维度 具体说明
核心角色 一个在 GLib 主循环 上运行的 套接字监听器,用于接受传入连接。
继承关系 继承自 GSocketListener ,需使用父类方法(如 add_inet_port)绑定地址和端口。
线程模型 本身不是线程安全 的,运行在创建它的线程的主循环中-1。但其 start()stop() 方法是线程安全的。
事件驱动 采用 异步、非阻塞 模式。新连接到达时,会发出 incoming 信号,处理函数必须立即返回以避免阻塞。

核心特性

  1. 自动连接管理:处理新的客户端连接

  2. 异步 I/O:基于 GIO 的异步操作,非阻塞

  3. 线程支持:可在工作线程中处理连接

  4. 多协议支持:TCP, Unix domain socket

🛠️ 如何使用 GSocketService

通常有两种使用方式:

  1. 信号连接 :创建服务后,连接 incoming 信号到一个处理函数。

  2. 子类化 :创建 GSocketService 的子类,并重写其默认的 incoming 信号处理函数。

一个典型的使用流程如下:

cpp 复制代码
// 创建服务
GSocketService *service = g_socket_service_new();

// 监听端口(例如8080)
g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service), 8080, NULL, NULL);

// 连接 incoming 信号
g_signal_connect(service, "incoming", G_CALLBACK(on_incoming_connection), NULL);

// 启动服务(默认创建时已启动,除非被停止过)
g_socket_service_start(service);

// 确保主循环运行
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
cpp 复制代码
/**
 * @brief 创建 DBus 代理监听的套接字服务
 * @param socketPath 套接字路径
 * @return 创建成功返回 GSocketService 指针,失败返回 nullptr
 */
GSocketService *KMDbusProxy::createSocketServer(const std::string &socketPath)
{
    if (std::error_code ec; fs::exists(socketPath, ec))
    {
        fs::remove(socketPath, ec);
    }

    // 创建socket服务
    GSocketService *service = g_socket_service_new();

    // 创建socket地址
    GSocketAddress *address = g_unix_socket_address_new(socketPath.c_str());

    // 添加地址到服务
    GError *error = nullptr;
    g_socket_listener_add_address(G_SOCKET_LISTENER(service),
                                  address,
                                  G_SOCKET_TYPE_STREAM,
                                  G_SOCKET_PROTOCOL_DEFAULT,
                                  nullptr,
                                  nullptr,
                                  &error);

    if (error)
    {
        KMError("failed to create dbusproxy socket server: " + std::string(error->message));
        g_error_free(error);
        g_object_unref(address);
        g_object_unref(service);
        return nullptr;
    }

    // 释放地址引用
    g_object_unref(address);

    // 连接新连接信号
    g_signal_connect(service, "incoming", G_CALLBACK(onNewConnection), this);

    // 启动服务
    g_socket_service_start(G_SOCKET_SERVICE(service));

    return service;
}

关键点 :在 incoming 信号的处理函数中,你只能启动对该连接的处理(例如,为其设置异步I/O操作),不能进行阻塞操作(如长时间计算或阻塞式读写)。

🔄 GSocketService 与 GThreadedSocketService

如果你的连接处理逻辑确实包含阻塞代码 (如复杂数据库查询、文件IO等),应该使用其子类 GThreadedSocketService

两者的主要区别如下:

对比项 GSocketService GThreadedSocketService
用途 处理连接本身非阻塞异步的服务。 处理连接逻辑包含阻塞操作的服务。
线程模型 在主循环线程处理连接。 为每个连接在独立的工作线程 中调用 run 信号处理函数。
处理程序要求 处理函数必须立即返回 处理函数可以阻塞,直到连接关闭。
适用场景 高并发、I/O密集型的异步服务。 包含阻塞I/O或耗时计算的连接处理。

创建 GThreadedSocketService 时可以指定线程池的最大线程数(max_threads),以控制并发量。

💡 开发注意事项

  • 服务状态 :新创建的服务默认是活动的。只有在调用 stop() 停止后,才需要再调用 start() 来重启。

  • 连接对象管理incoming 信号返回后,传入的 GSocketConnection 对象的引用计数会被减少。如果需要在信号返回后继续使用该连接,必须手动调用 g_object_ref() 增加其引用计数。

  • 关闭监听器 :调用 stop() 只是停止接受新连接,不会关闭底层监听套接字。要完全关闭,需要调用从 GSocketListener 继承的 close() 方法。

关键 API

GSocketService 方法:

  • g_socket_service_new() - 创建新服务

  • g_socket_service_start() - 启动服务

  • g_socket_service_stop() - 停止服务

  • g_socket_service_is_active() - 检查服务是否活动

GSocketListener 方法(继承):

  • g_socket_listener_add_inet_port() - 监听 TCP 端口

  • g_socket_listener_add_address() - 监听特定地址

  • g_socket_listener_add_any_inet_port() - 监听任意可用端口

信号:

  • "incoming" - 新连接到达时触发

注意事项

  1. 引用计数:注意正确管理 GObject 引用计数

  2. 错误处理:总是检查 GError 返回值

  3. 资源清理:及时关闭流和释放资源

  4. 线程安全:如果需要跨线程访问,使用线程安全的数据结构

  5. 异步操作:推荐使用异步 I/O 避免阻塞主循环

基本使用步骤

1. 创建服务

cpp 复制代码
GSocketService *service = g_socket_service_new();

2. 监听端口

cpp 复制代码
// TCP 监听
g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service), 
                                port, 
                                NULL, 
                                &error);

// Unix domain socket 监听
g_socket_listener_add_address(G_SOCKET_LISTENER(service),
                              address,
                              G_SOCKET_TYPE_STREAM,
                              G_SOCKET_PROTOCOL_TCP,
                              NULL,
                              NULL,
                              &error);

3. 连接信号处理

cpp 复制代码
g_signal_connect(service, "incoming", G_CALLBACK(on_incoming_connection), user_data);

4. 启动服务

cpp 复制代码
g_socket_service_start(service);

完整示例

示例 1: 简单的 Echo 服务器

cpp 复制代码
#include <glib.h>
#include <gio/gio.h>
#include <string.h>

#define PORT 12345

// 处理接收到的数据
static void on_data_received(GObject *source_object,
                            GAsyncResult *res,
                            gpointer user_data) {
    GInputStream *istream = G_INPUT_STREAM(source_object);
    GOutputStream *ostream = G_OUTPUT_STREAM(user_data);
    GError *error = NULL;
    gchar buffer[1024];
    gssize size;

    // 完成读取操作
    size = g_input_stream_read_finish(istream, res, &error);
    
    if (size > 0) {
        // 回显数据
        g_output_stream_write(ostream, buffer, size, NULL, &error);
        
        // 继续读取
        g_input_stream_read_async(istream,
                                 buffer,
                                 sizeof(buffer),
                                 G_PRIORITY_DEFAULT,
                                 NULL,
                                 on_data_received,
                                 ostream);
    } else {
        if (error) {
            g_printerr("Read error: %s\n", error->message);
            g_error_free(error);
        }
        // 关闭连接
        g_input_stream_close(istream, NULL, NULL);
        g_output_stream_close(ostream, NULL, NULL);
        g_object_unref(istream);
        g_object_unref(ostream);
    }
}

// 处理新连接
static gboolean on_incoming_connection(GSocketService *service,
                                      GSocketConnection *connection,
                                      GObject *source_object,
                                      gpointer user_data) {
    GInputStream *istream;
    GOutputStream *ostream;
    gchar buffer[1024];

    g_print("New connection from client\n");

    // 获取输入输出流
    istream = g_io_stream_get_input_stream(G_IO_STREAM(connection));
    ostream = g_io_stream_get_output_stream(G_IO_STREAM(connection));

    // 开始异步读取数据
    g_input_stream_read_async(istream,
                             buffer,
                             sizeof(buffer),
                             G_PRIORITY_DEFAULT,
                             NULL,
                             on_data_received,
                             ostream);

    // 保持连接引用,防止被回收
    g_object_ref(connection);
    
    return TRUE; // 返回 TRUE 表示接受连接
}

int main(int argc, char *argv[]) {
    GSocketService *service;
    GError *error = NULL;
    GMainLoop *loop;

    // 创建 socket 服务
    service = g_socket_service_new();

    // 监听端口
    if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service),
                                        PORT,
                                        NULL,
                                        &error)) {
        g_printerr("Failed to listen on port %d: %s\n", PORT, error->message);
        g_error_free(error);
        return 1;
    }

    g_print("Echo server listening on port %d\n", PORT);

    // 连接信号
    g_signal_connect(service, "incoming", 
                    G_CALLBACK(on_incoming_connection), NULL);

    // 启动服务
    g_socket_service_start(service);

    // 运行主循环
    loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);

    // 清理
    g_main_loop_unref(loop);
    g_object_unref(service);

    return 0;
}

示例 2: HTTP 服务器

cpp 复制代码
#include <glib.h>
#include <gio/gio.h>
#include <string.h>

#define PORT 8080

// HTTP 响应
static const gchar *HTTP_RESPONSE = 
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/html; charset=UTF-8\r\n"
    "Content-Length: %ld\r\n"
    "\r\n"
    "<html><head><title>Test Server</title></head>"
    "<body><h1>Hello from GSocketService!</h1>"
    "<p>Current time: %s</p></body></html>";

// 处理 HTTP 请求
static void handle_http_request(GSocketConnection *connection,
                               const gchar *request) {
    GOutputStream *ostream;
    GDateTime *now;
    gchar *time_str;
    gchar *response_body;
    gchar *response;
    gsize response_len;
    GError *error = NULL;

    // 获取当前时间
    now = g_date_time_new_now_local();
    time_str = g_date_time_format(now, "%Y-%m-%d %H:%M:%S");
    g_date_time_unref(now);

    // 创建响应体
    response_body = g_strdup_printf("<html><body><h1>Hello!</h1><p>Time: %s</p>"
                                   "<p>Request: %s</p></body></html>",
                                   time_str, request);
    g_free(time_str);

    // 创建完整 HTTP 响应
    response_len = strlen(response_body);
    response = g_strdup_printf(HTTP_RESPONSE, response_len, response_body);

    // 发送响应
    ostream = g_io_stream_get_output_stream(G_IO_STREAM(connection));
    g_output_stream_write_all(ostream,
                             response,
                             strlen(response),
                             NULL,
                             NULL,
                             &error);

    if (error) {
        g_printerr("Write error: %s\n", error->message);
        g_error_free(error);
    }

    // 清理
    g_free(response_body);
    g_free(response);
    g_output_stream_close(ostream, NULL, NULL);
    g_object_unref(connection);
}

// 读取完整请求
static void on_request_read(GObject *source_object,
                           GAsyncResult *res,
                           gpointer user_data) {
    GInputStream *istream = G_INPUT_STREAM(source_object);
    GSocketConnection *connection = G_SOCKET_CONNECTION(user_data);
    GError *error = NULL;
    gchar *buffer;
    gssize size;

    size = g_input_stream_read_finish(istream, res, &error);
    
    if (size > 0) {
        buffer = g_strndup((gchar*)g_bytes_get_data(g_bytes_new_static(buffer, size), NULL), size);
        g_print("Received request:\n%s\n", buffer);
        
        // 处理 HTTP 请求
        handle_http_request(connection, buffer);
        
        g_free(buffer);
    } else {
        if (error) {
            g_printerr("Read error: %s\n", error->message);
            g_error_free(error);
        }
        g_object_unref(connection);
    }
    
    g_object_unref(istream);
}

// 处理新连接
static gboolean on_incoming_connection(GSocketService *service,
                                      GSocketConnection *connection,
                                      GObject *source_object,
                                      gpointer user_data) {
    GInputStream *istream;
    gchar buffer[4096];

    g_print("New HTTP connection\n");

    istream = g_io_stream_get_input_stream(G_IO_STREAM(connection));
    
    // 异步读取请求
    g_input_stream_read_async(istream,
                             buffer,
                             sizeof(buffer),
                             G_PRIORITY_DEFAULT,
                             NULL,
                             on_request_read,
                             g_object_ref(connection));

    return TRUE;
}

int main(int argc, char *argv[]) {
    GSocketService *service;
    GError *error = NULL;
    GMainLoop *loop;

    // 创建服务
    service = g_socket_service_new();

    // 监听端口
    if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service),
                                        PORT,
                                        NULL,
                                        &error)) {
        g_printerr("Failed to listen on port %d: %s\n", PORT, error->message);
        g_error_free(error);
        return 1;
    }

    g_print("HTTP server listening on port %d\n", PORT);

    // 连接信号
    g_signal_connect(service, "incoming",
                    G_CALLBACK(on_incoming_connection), NULL);

    // 启动服务
    g_socket_service_start(service);

    // 运行主循环
    loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);

    // 清理
    g_main_loop_unref(loop);
    g_object_unref(service);

    return 0;
}

示例 3: 带线程池的服务

cpp 复制代码
#include <glib.h>
#include <gio/gio.h>
#include <string.h>

#define PORT 9999
#define MAX_THREADS 10

typedef struct {
    GSocketConnection *connection;
    GSocketService *service;
} ThreadData;

// 工作线程函数
static gpointer worker_thread(gpointer data) {
    ThreadData *tdata = (ThreadData *)data;
    GInputStream *istream;
    GOutputStream *ostream;
    gchar buffer[1024];
    gssize bytes_read;
    GError *error = NULL;

    g_print("Thread %p handling connection\n", g_thread_self());

    istream = g_io_stream_get_input_stream(G_IO_STREAM(tdata->connection));
    ostream = g_io_stream_get_output_stream(G_IO_STREAM(tdata->connection));

    // 读取数据
    while ((bytes_read = g_input_stream_read(istream,
                                            buffer,
                                            sizeof(buffer),
                                            NULL,
                                            &error)) > 0) {
        // 处理数据(这里简单回显)
        g_output_stream_write(ostream,
                             buffer,
                             bytes_read,
                             NULL,
                             &error);
        
        if (error) {
            g_printerr("Write error: %s\n", error->message);
            g_error_free(error);
            break;
        }
    }

    if (error) {
        g_printerr("Read error: %s\n", error->message);
        g_error_free(error);
    }

    // 清理
    g_object_unref(tdata->connection);
    g_free(tdata);

    return NULL;
}

// 处理新连接(在工作线程中处理)
static gboolean on_incoming_connection(GSocketService *service,
                                      GSocketConnection *connection,
                                      GObject *source_object,
                                      gpointer user_data) {
    GThreadPool *pool = (GThreadPool *)user_data;
    ThreadData *tdata;

    g_print("New connection, queuing for thread pool\n");

    // 创建线程数据
    tdata = g_new(ThreadData, 1);
    tdata->connection = g_object_ref(connection);
    tdata->service = service;

    // 将工作加入线程池
    g_thread_pool_push(pool, tdata, NULL);

    return TRUE;
}

int main(int argc, char *argv[]) {
    GSocketService *service;
    GError *error = NULL;
    GMainLoop *loop;
    GThreadPool *pool;

    // 创建线程池
    pool = g_thread_pool_new(worker_thread,
                            NULL,
                            MAX_THREADS,
                            FALSE,
                            &error);
    
    if (error) {
        g_printerr("Failed to create thread pool: %s\n", error->message);
        g_error_free(error);
        return 1;
    }

    // 创建 socket 服务
    service = g_socket_service_new();

    // 监听端口
    if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service),
                                        PORT,
                                        NULL,
                                        &error)) {
        g_printerr("Failed to listen on port %d: %s\n", PORT, error->message);
        g_error_free(error);
        return 1;
    }

    g_print("Threaded server listening on port %d\n", PORT);

    // 连接信号
    g_signal_connect(service, "incoming",
                    G_CALLBACK(on_incoming_connection), pool);

    // 启动服务
    g_socket_service_start(service);

    // 运行主循环
    loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);

    // 清理
    g_thread_pool_free(pool, FALSE, TRUE);
    g_main_loop_unref(loop);
    g_object_unref(service);

    return 0;
}
相关推荐
Trouvaille ~2 小时前
【Linux】进程信号(一):信号的快速认识与五种产生方式
linux·运维·网络·c++·操作系统·信号处理·中断
线束线缆组件品替网2 小时前
Stewart Connector RJ45 以太网线缆高速接口设计解析
服务器·网络·人工智能·音视频·硬件工程·材料工程
凯子坚持 c2 小时前
Qt常用控件指南(3)
运维·服务器
闲过信陵饮~2 小时前
ubuntu24 安装向日葵远程软件报错
linux·运维·ubuntu
Dovis(誓平步青云)2 小时前
《优化算法效率的利器:双指针的原理、变种与边界处理》
linux·运维·算法·功能详解
aaa最北边2 小时前
进程间通信-1.管道通信
android·java·服务器
188号安全攻城狮2 小时前
【PWN】HappyNewYearCTF_2_栈上变量覆写1
linux·运维·汇编·安全·网络安全
头发还没掉光光2 小时前
解决TCP粘包问题,使用C++实现TCP通信的自定义协议设计
linux·网络·c++·网络协议·tcp/ip
翱翔的苍鹰3 小时前
智谱(Zhipu)大模型的流式使用 response.iter_lines() 逐行解析 SSE 流
服务器·前端·数据库