Linux에서 dbus-c++바인딩으로 서비스를 제공하는 프로세스 구현 방법을 소개한다.
참고로 dbus-c++ 바인더는 2011년 이후로 개발이 중지된 상태이지만 여기선 간단히 개념을 설명하기 위해 사용하였다. 현재 C++위한 바인더로 dbus-cxx, dbus-cpp, Qt D-Bus, GDBus 등이 있다.
먼저 아래명령으로 c++바인딩 패키지를 설치한다.
apt-get install libdbus-c++-bin libdbus-c++-dev
작업 폴더를 만들고 아래의 예제처럼 xml로 인터페이스를 작성한다.
아래의 예제에서 type="s" name="name" direction="in" 이것은 string타입의 입력 인자를 의미한다.
interface.xml
<?xml version="1.0" ?> <node name="/org/freedesktop/DBus/Examples/Echo"> <interface name="org.freedesktop.DBus.EchoDemo"> <method name="HelloString"> <arg type="s" name="name" direction="in"/> <arg type="s" name="greeting" direction="out"/> </method> <signal name="EchoCount"> <arg type="y" name="count"/> </signal> </interface> </node>
아래표는 D-Bus에서 사용하는 타입들이다.
Category |
Conventional Name |
Code |
Description |
reserved |
INVALID |
0 (ASCII NUL) |
Not a valid type code, used to terminate signatures |
fixed, basic |
BYTE |
121 (ASCII 'y') |
8-bit unsigned integer |
fixed, basic |
BOOLEAN |
98 (ASCII 'b') |
Boolean value, 0 is FALSE and 1 is TRUE. Everything else is invalid. |
fixed, basic |
INT16 |
110 (ASCII 'n') |
16-bit signed integer |
fixed, basic |
UINT16 |
113 (ASCII 'q') |
16-bit unsigned integer |
fixed, basic |
INT32 |
105 (ASCII 'i') |
32-bit signed integer |
fixed, basic |
UINT32 |
117 (ASCII 'u') |
32-bit unsigned integer |
fixed, basic |
INT64 |
120 (ASCII 'x') |
64-bit signed integer |
fixed, basic |
UINT64 |
116 (ASCII 't') |
64-bit unsigned integer |
fixed, basic |
DOUBLE |
100 (ASCII 'd') |
IEEE 754 double |
string-like, basic |
STRING |
115 (ASCII 's') |
UTF-8 string (must be valid UTF-8). Must be nul terminated and contain no other nul bytes. |
string-like, basic |
OBJECT_PATH |
111 (ASCII 'o') |
Name of an object instance |
string-like, basic |
SIGNATURE |
103 (ASCII 'g') |
A type signature |
container |
ARRAY |
97 (ASCII 'a') |
Array |
container |
STRUCT |
114 (ASCII 'r'), 40 (ASCII '('), 41 (ASCII ')') |
Struct; type code 114 'r' is reserved for use in bindings and implementations to represent the general concept of a struct, and must not appear in signatures used on D-Bus. |
container |
VARIANT |
118 (ASCII 'v') |
Variant type (the type of the value is part of the value itself) |
container |
DICT_ENTRY |
101 (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}') |
Entry in a dict or map (array of key-value pairs). Type code 101 'e' is reserved for use in bindings and implementations to represent the general concept of a dict or dict-entry, and must not appear in signatures used on D-Bus. |
fixed, basic |
UNIX_FD |
104 (ASCII 'h') |
Unix file descriptor |
reserved |
(reserved) |
109 (ASCII 'm') |
Reserved for a 'maybe' type compatible with the one in GVariant, and must not appear in signatures used on D-Bus until specified here |
reserved |
(reserved) |
42 (ASCII '*') |
Reserved for use in bindings/implementations to represent any single complete type, and must not appear in signatures used on D-Bus. |
reserved |
(reserved) |
63 (ASCII '?') |
Reserved for use in bindings/implementations to represent any basic type, and must not appear in signatures used on D-Bus. |
reserved |
(reserved) |
64 (ASCII '@'), 38 (ASCII '&'), 94 (ASCII '^') |
Reserved for internal use by bindings/implementations, and must not appear in signatures used on D-Bus. GVariant uses these type-codes to encode calling conventions. |
작성된 xml을 service를 제공하는 쪽에서 사용 할 헤더파일로 변환한다.
dbusxx-xml2cpp interface.xml --adaptor=echo-interface-adaptor.h
변환된 헤더파일은 대략 아래와 같다.
echo-interface-adaptor.h
/* * This file was automatically generated by dbusxx-xml2cpp; DO NOT EDIT! */ #ifndef __dbusxx__echo_interface_adaptor_h__ADAPTOR_MARSHAL_H #define __dbusxx__echo_interface_adaptor_h__ADAPTOR_MARSHAL_H #include <dbus-c++/dbus.h> #include <cassert> namespace org { namespace freedesktop { namespace DBus { class EchoDemo_adaptor : public ::DBus::InterfaceAdaptor { public: EchoDemo_adaptor() : ::DBus::InterfaceAdaptor("org.freedesktop.DBus.EchoDemo") { register_method(EchoDemo_adaptor, HelloString, _HelloString_stub); } ::DBus::IntrospectedInterface *introspect() const { static ::DBus::IntrospectedArgument HelloString_args[] = { { "name", "s", true }, { "greeting", "s", false }, { 0, 0, 0 } }; static ::DBus::IntrospectedArgument EchoCount_args[] = { { "count", "y", false }, { 0, 0, 0 } }; static ::DBus::IntrospectedMethod EchoDemo_adaptor_methods[] = { { "HelloString", HelloString_args }, { 0, 0 } }; static ::DBus::IntrospectedMethod EchoDemo_adaptor_signals[] = { { "EchoCount", EchoCount_args }, { 0, 0 } }; static ::DBus::IntrospectedProperty EchoDemo_adaptor_properties[] = { { 0, 0, 0, 0 } }; static ::DBus::IntrospectedInterface EchoDemo_adaptor_interface = { "org.freedesktop.DBus.EchoDemo", EchoDemo_adaptor_methods, EchoDemo_adaptor_signals, EchoDemo_adaptor_properties }; return &EchoDemo_adaptor_interface; } public: /* properties exposed by this interface, use * property() and property(value) to get and set a particular property */ public: /* methods exported by this interface, * you will have to implement them in your ObjectAdaptor */ virtual std::string HelloString(const std::string& name) = 0; public: /* signal emitters for this interface */ void EchoCount(const uint8_t& arg1) { ::DBus::SignalMessage sig("EchoCount"); ::DBus::MessageIter wi = sig.writer(); wi << arg1; emit_signal(sig); } private: /* unmarshalers (to unpack the DBus message before calling the actual interface method) */ ::DBus::Message _HelloString_stub(const ::DBus::CallMessage &call) { ::DBus::MessageIter ri = call.reader(); std::string argin1; ri >> argin1; std::string argout1 = HelloString(argin1); ::DBus::ReturnMessage reply(call); ::DBus::MessageIter wi = reply.writer(); wi << argout1; return reply; } }; } } } #endif //__dbusxx__echo_interface_adaptor_h__ADAPTOR_MARSHAL_H
이제 위의 클래스를 상속받고 가상함수를 오버라이드하여 정의한다.
echo-server.h
#include "echo-interface-adaptor.h" class EchoServer : public org::freedesktop::DBus::EchoDemo_adaptor, public DBus::IntrospectableAdaptor, public DBus::ObjectAdaptor { public: EchoServer(DBus::Connection * conn); virtual ~EchoServer(); public: virtual std::string HelloString(const std::string& name); };
echo-server.cpp
#include "echo-server.h" #include <stdio.h> EchoServer::EchoServer(DBus::Connection * conn) : DBus::ObjectAdaptor(*conn, "/org/freedesktop/DBus/Examples/Echo") { } EchoServer::~EchoServer() { } std::string EchoServer::HelloString(const std::string& name) { printf("Input string : %s\n", name.c_str() ); std::string ret = name + " world!!"; return ret; }
main함수에서는 service를 위한 SessionBus 연결 및 객체를 생성한다.
main.cpp
#include "echo-server.h" #include <unistd.h> #include <future> #include <stdio.h> DBus::BusDispatcher dispatcher; int main() { DBus::default_dispatcher = &dispatcher; DBus::_init_threading(); DBus::Connection conn = DBus::Connection::SessionBus(); conn.request_name("org.freedesktop.DBus.EchoService"); EchoServer server(&conn); uint8_t count = 0; std::future<void>fut = std::async(std::launch::async, [=] { dispatcher.enter(); }); // emit EchoSignal every seconds. while ( true ) { printf("emit signal 'EchoSignal' count : %d\n", count); server.EchoCount(count++); sleep(1); } return 0; }
dbus-c++-1 라이브러리 및 INCLUDE Path를 컴파일 시에 인자로 넘겨준다.
이제 구현 테스트를 위한 방법으로 우분투 소프트웨어 센터에서 "D-Feet" 검색하고 설치한다.
어플리케이션을 실행한 상태에서 D-Feet을 실행하고 Session Bus 탭을 선택 후 "echo"를 검색하면 아래처럼 보일 것이다.
오른쪽 Object path영역에서 인터페이스를 펼치면 아래처럼 서비스쪽의 Methods 및 Signals 등이 보인다.
Methods에서 HelloString을 더블클릭한다.
Input영역에 문자열 입력 후 Execute를 클릭한다.
그러면 service 쪽 출력내용이 다음과 같이 보일 것이다.
이 서비스 프로세스와 통신하는 클라이언트는 파이썬으로 구현, 테스트 예제: https://makersweb.net/python/6517
윈도우에서 Qt D-Bus를 사용한 예제: https://makersweb.net/qt/13174
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
25 | libblkid - USB Storage의 정보 가져오기 | makersweb | 2018.10.18 | 1213 |
24 | tslib 크로스 컴파일과 터치스크린 보정 | makersweb | 2018.08.02 | 2666 |
» | Ubuntu Linux에서 dbus-c++바인딩 D-Bus 테스트 | makersweb | 2018.03.07 | 7675 |
22 | NFS를 통해 파일시스템 공유 | makersweb | 2018.03.05 | 1590 |
21 | Wayland에 대한 간단한 소개 | makersweb | 2017.12.29 | 4015 |
20 | Ubuntu16.04에서 weston구동 | makersweb | 2017.12.28 | 1470 |
19 | UVC 장치를 사용할때 v4l2: select timeout 에러 발생 | makersweb | 2017.12.27 | 2901 |
18 | [IPC]D-Bus 소개 | makersweb | 2015.02.28 | 30400 |
17 | 리눅스 데스크탑 환경 종류 | pjk | 2015.02.11 | 5097 |
16 | 디바이스 드라이버에 대해서 | makersweb | 2014.04.19 | 4600 |
15 | 리눅스 커널 소스코드 구성도 | makersweb | 2014.03.04 | 5932 |
14 | read() 함수, write() 함수 | makersweb | 2014.03.04 | 11819 |
13 | 리눅스 디렉터리 구조 | makersweb | 2014.02.28 | 3664 |
12 | 1. make | pjk | 2014.02.05 | 2526 |
11 | 2. 간단한 Makefile | pjk | 2014.02.05 | 2974 |
10 | 3. 매크로(Macro) 와 확장자(Suffix) 규칙 | pjk | 2014.02.05 | 3003 |
9 | 4. Makefile를 작성할 때 알면 좋은 것들 | pjk | 2014.02.05 | 3484 |
8 | 5. make 중요 옵션 정리 | pjk | 2014.02.05 | 2884 |
7 | 6. Makefile 작성의 가이드라인 | pjk | 2014.02.05 | 2777 |
6 | mmap() 함수, munmap() 함수 | pjk | 2014.02.05 | 16586 |