반응형

GLib을 사용한 GDBus 예제 (Client/Server 구조)

다음은 GLib의 GDBus를 사용한 간단한 클라이언트-서버 예제입니다. 이 예제에서는 서버가 계산기 서비스를 제공하고 클라이언트가 그 서비스를 사용합니다.

서버 코드 (server.c)

#include <glib.h>
#include <gio/gio.h>

/* 인터페이스 XML 정의 */
static const gchar *interface_xml =
  "<node>"
  "  <interface name='org.example.Calculator'>"
  "    <method name='Add'>"
  "      <arg type='d' name='a' direction='in'/>"
  "      <arg type='d' name='b' direction='in'/>"
  "      <arg type='d' name='result' direction='out'/>"
  "    </method>"
  "    <method name='Subtract'>"
  "      <arg type='d' name='a' direction='in'/>"
  "      <arg type='d' name='b' direction='in'/>"
  "      <arg type='d' name='result' direction='out'/>"
  "    </method>"
  "    <signal name='CalculationDone'>"
  "      <arg type='d' name='result'/>"
  "    </signal>"
  "    <property name='LastResult' type='d' access='read'/>"
  "  </interface>"
  "</node>";

static GDBusNodeInfo *introspection_data = NULL;
static gdouble last_result = 0.0;

/* 메소드 호출 처리 */
static void handle_method_call(GDBusConnection *connection,
                              const gchar *sender,
                              const gchar *object_path,
                              const gchar *interface_name,
                              const gchar *method_name,
                              GVariant *parameters,
                              GDBusMethodInvocation *invocation,
                              gpointer user_data)
{
  if (g_strcmp0(interface_name, "org.example.Calculator") == 0)
  {
    if (g_strcmp0(method_name, "Add") == 0)
    {
      gdouble a, b;
      g_variant_get(parameters, "(dd)", &a, &b);
      last_result = a + b;
      
      // 결과 반환
      g_dbus_method_invocation_return_value(invocation, 
                                           g_variant_new("(d)", last_result));
      
      // 시그널 발송
      g_dbus_connection_emit_signal(connection,
                                  NULL,
                                  object_path,
                                  "org.example.Calculator",
                                  "CalculationDone",
                                  g_variant_new("(d)", last_result),
                                  NULL);
    }
    else if (g_strcmp0(method_name, "Subtract") == 0)
    {
      gdouble a, b;
      g_variant_get(parameters, "(dd)", &a, &b);
      last_result = a - b;
      g_dbus_method_invocation_return_value(invocation, 
                                          g_variant_new("(d)", last_result));
      
      g_dbus_connection_emit_signal(connection,
                                   NULL,
                                   object_path,
                                   "org.example.Calculator",
                                   "CalculationDone",
                                   g_variant_new("(d)", last_result),
                                   NULL);
    }
  }
}

/* 프로퍼티 가져오기 처리 */
static GVariant *handle_get_property(GDBusConnection *connection,
                                    const gchar *sender,
                                    const gchar *object_path,
                                    const gchar *interface_name,
                                    const gchar *property_name,
                                    GError **error,
                                    gpointer user_data)
{
  if (g_strcmp0(interface_name, "org.example.Calculator") == 0)
  {
    if (g_strcmp0(property_name, "LastResult") == 0)
    {
      return g_variant_new_double(last_result);
    }
  }
  
  g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown property");
  return NULL;
}

int main(int argc, char *argv[])
{
  GMainLoop *loop;
  GDBusConnection *connection;
  GError *error = NULL;
  guint owner_id;
  
  g_type_init();
  
  loop = g_main_loop_new(NULL, FALSE);
  
  // 인트로스펙션 데이터 생성
  introspection_data = g_dbus_node_info_new_for_xml(interface_xml, NULL);
  
  // D-Bus에 서비스 등록
  owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
                           "org.example.CalculatorService",
                           G_BUS_NAME_OWNER_FLAGS_NONE,
                           NULL, /* on_bus_name_acquired */
                           NULL, /* on_name_acquired */
                           NULL, /* on_name_lost */
                           NULL, /* user_data */
                           NULL); /* user_data_free_func */
  
  connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
  if (connection == NULL)
  {
    g_printerr("Failed to connect to D-Bus: %s\n", error->message);
    g_error_free(error);
    return 1;
  }
  
  // 객체 매니저 등록
  GDBusInterfaceVTable interface_vtable = {
    handle_method_call,
    handle_get_property,
    NULL /* set_property */
  };
  
  guint registration_id = g_dbus_connection_register_object(connection,
                                                           "/org/example/Calculator",
                                                           introspection_data->interfaces[0],
                                                           &interface_vtable,
                                                           NULL, /* user_data */
                                                           NULL, /* user_data_free_func */
                                                           &error);
  if (registration_id == 0)
  {
    g_printerr("Failed to register object: %s\n", error->message);
    g_error_free(error);
    return 1;
  }
  
  g_print("Calculator service is running...\n");
  g_main_loop_run(loop);
  
  // 정리
  g_dbus_connection_unregister_object(connection, registration_id);
  g_bus_unown_name(owner_id);
  g_object_unref(connection);
  g_main_loop_unref(loop);
  g_dbus_node_info_unref(introspection_data);
  
  return 0;
}

클라이언트 코드 (client.c)

#include <glib.h>
#include <gio/gio.h>

static GMainLoop *loop;

/* 시그널 핸들러 */
static void on_calculation_done(GDBusConnection *connection,
                               const gchar *sender_name,
                               const gchar *object_path,
                               const gchar *interface_name,
                               const gchar *signal_name,
                               GVariant *parameters,
                               gpointer user_data)
{
  gdouble result;
  g_variant_get(parameters, "(d)", &result);
  g_print("Received CalculationDone signal: Result is %g\n", result);
}

/* 비동기 호출 완료 콜백 */
static void on_call_finished(GObject *source_object,
                            GAsyncResult *res,
                            gpointer user_data)
{
  GDBusConnection *connection = G_DBUS_CONNECTION(source_object);
  GVariant *result;
  GError *error = NULL;
  
  result = g_dbus_connection_call_finish(connection, res, &error);
  if (error != NULL)
  {
    g_printerr("Error calling method: %s\n", error->message);
    g_error_free(error);
    g_main_loop_quit(loop);
    return;
  }
  
  gdouble out;
  g_variant_get(result, "(d)", &out);
  g_print("Method call result: %g\n", out);
  g_variant_unref(result);
  
  // 프로퍼티 읽기
  GVariant *property_value = g_dbus_connection_call_sync(connection,
                                                        "org.example.CalculatorService",
                                                        "/org/example/Calculator",
                                                        "org.freedesktop.DBus.Properties",
                                                        "Get",
                                                        g_variant_new("(ss)",
                                                                     "org.example.Calculator",
                                                                     "LastResult"),
                                                        NULL,
                                                        G_DBUS_CALL_FLAGS_NONE,
                                                        -1,
                                                        NULL,
                                                        &error);
  if (property_value == NULL)
  {
    g_printerr("Error getting property: %s\n", error->message);
    g_error_free(error);
  }
  else
  {
    GVariant *value;
    g_variant_get(property_value, "(v)", &value);
    g_print("LastResult property: %g\n", g_variant_get_double(value));
    g_variant_unref(value);
    g_variant_unref(property_value);
  }
  
  g_main_loop_quit(loop);
}

int main(int argc, char *argv[])
{
  GDBusConnection *connection;
  GError *error = NULL;
  guint signal_subscription_id;
  
  g_type_init();
  
  loop = g_main_loop_new(NULL, FALSE);
  
  connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
  if (connection == NULL)
  {
    g_printerr("Failed to connect to D-Bus: %s\n", error->message);
    g_error_free(error);
    return 1;
  }
  
  // 시그널 구독
  signal_subscription_id = g_dbus_connection_signal_subscribe(connection,
                                                            "org.example.CalculatorService",
                                                            "org.example.Calculator",
                                                            "CalculationDone",
                                                            "/org/example/Calculator",
                                                            NULL,
                                                            G_DBUS_SIGNAL_FLAGS_NONE,
                                                            on_calculation_done,
                                                            NULL,
                                                            NULL);
  
  // 비동기 메소드 호출
  g_dbus_connection_call(connection,
                        "org.example.CalculatorService",
                        "/org/example/Calculator",
                        "org.example.Calculator",
                        "Add",
                        g_variant_new("(dd)", 10.5, 20.3),
                        NULL,
                        G_DBUS_CALL_FLAGS_NONE,
                        -1,
                        NULL,
                        on_call_finished,
                        NULL);
  
  g_print("Waiting for method call to complete...\n");
  g_main_loop_run(loop);
  
  // 정리
  g_dbus_connection_signal_unsubscribe(connection, signal_subscription_id);
  g_object_unref(connection);
  g_main_loop_unref(loop);
  
  return 0;
}

컴파일 및 실행 방법

  1. 필요한 패키지 설치 (Ubuntu 기준):
 
sudo apt-get install libglib2.0-dev libgio2.0-dev
  1. 컴파일:
 
gcc -o server server.c `pkg-config --cflags --libs gio-2.0`
gcc -o client client.c `pkg-config --cflags --libs gio-2.0`
  1. 실행:
  • 먼저 서버를 실행합니다:
 
./server
  • 다른 터미널에서 클라이언트를 실행합니다:
 
./client

예제 설명

  1. 서버:
    • org.example.Calculator 인터페이스를 정의하고 D-Bus에 등록
    • Add와 Subtract 메소드 제공
    • CalculationDone 시그널과 LastResult 프로퍼티 제공
    • 세션 버스를 통해 org.example.CalculatorService 이름으로 서비스 제공
  2. 클라이언트:
    • 서버의 Add 메소드를 비동기적으로 호출
    • CalculationDone 시그널을 구독하여 결과 알림 수신
    • 호출 완료 후 LastResult 프로퍼티 조회

이 예제는 GLib의 GDBus API를 사용하여 D-Bus 서비스와 클라이언트를 구현하는 기본적인 방법을 보여줍니다. 실제 애플리케이션에서는 더 많은 오류 처리와 복잡한 기능이 필요할 수 있습니다.

반응형

'D-BUS' 카테고리의 다른 글

(비) [D-BUS] 간단한 예제 CMakefile.txt 작성법 - bb file 작성법  (0) 2025.03.28
[D-BUS] D-BUS란?  (0) 2025.03.24