GSocketService 是 GLib/GIO 库中的一个核心类,用于简化异步网络服务或本地套接字服务的创建。它工作在 GLib 的主事件循环上,能高效地处理并发连接。
🧩 GSocketService 核心解读
你可以通过下面的表格快速了解它的关键特性:
| 特性维度 | 具体说明 |
|---|---|
| 核心角色 | 一个在 GLib 主循环 上运行的 套接字监听器,用于接受传入连接。 |
| 继承关系 | 继承自 GSocketListener ,需使用父类方法(如 add_inet_port)绑定地址和端口。 |
| 线程模型 | 本身不是线程安全 的,运行在创建它的线程的主循环中-1。但其 start() 和 stop() 方法是线程安全的。 |
| 事件驱动 | 采用 异步、非阻塞 模式。新连接到达时,会发出 incoming 信号,处理函数必须立即返回以避免阻塞。 |
核心特性
-
自动连接管理:处理新的客户端连接
-
异步 I/O:基于 GIO 的异步操作,非阻塞
-
线程支持:可在工作线程中处理连接
-
多协议支持:TCP, Unix domain socket
🛠️ 如何使用 GSocketService
通常有两种使用方式:
-
信号连接 :创建服务后,连接
incoming信号到一个处理函数。 -
子类化 :创建
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"- 新连接到达时触发
注意事项
-
引用计数:注意正确管理 GObject 引用计数
-
错误处理:总是检查 GError 返回值
-
资源清理:及时关闭流和释放资源
-
线程安全:如果需要跨线程访问,使用线程安全的数据结构
-
异步操作:推荐使用异步 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;
}