Document directory
- 5.1 code
- 5.2 execution
- 6.1. Configure. AC
- 6.2. src/makefile. AM
4. complex data types
How does one process complex data types in a shard? The first suggestion is to try not to use complex data types. But what if they do? Some netizens suggest using garray as a container. Whatever the parameter, They manually put garray on the client side and retrieve it on the server side. This is indeed an idea and is suitable for self-developed servers and clients. Another article "How to pass a variant with your-glib" describes how to use gvalue to transmit complex data types. For more information, see.
The following describes how to process the {SV} parameter in our example:
$ cat sms_features.h#ifndef SMS_FEATURES_H#define SMS_FEATURES_H#include <glib-object.h>GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq);GType sms_get_features_type(void);void sms_release_features(GHashTable *features);void sms_show_features(GHashTable *features);#endif
Sms_features.h declares several functions. In this example, both the server and client call. The following are the implementations of these functions:
$ cat -n sms_features.c 1 #include "sms_features.h" 2 3 static void release_val(gpointer data) 4 { 5 GValue *val = (GValue *)data; 6 g_value_unset(val); 7 g_free(val); 8 } 9 10 GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq) 11 { 12 GHashTable *hash; 13 GValue *val; 14 15 hash = g_hash_table_new_full (g_str_hash, NULL, NULL, release_val); 16 17 val = g_new0(GValue, 1); 18 g_value_init (val, G_TYPE_STRING); 19 g_value_set_string (val, alphabet); 20 g_hash_table_insert(hash, "alphabet", val); 21 22 val = g_new0(GValue, 1); 23 g_value_init (val, G_TYPE_INT); 24 g_value_set_int (val, csm_num); 25 g_hash_table_insert(hash, "csm_num", val); 26 27 val = g_new0(GValue, 1); 28 g_value_init (val, G_TYPE_INT); 29 g_value_set_int (val, csm_seq); 30 g_hash_table_insert(hash, "csm_seq", val); 31 32 return hash; 33 } 34 35 GType sms_get_features_type(void) 36 { 37 return dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE); 38 } 39 40 void sms_show_features(GHashTable *features) 41 { 42 GList *keys = g_hash_table_get_keys(features); 43 gint len = g_list_length(keys); 44 gint i; 45 46 for (i = 0; i < len; i++) { 47 gchar *key = g_list_nth_data(keys, i); 48 GValue *val = g_hash_table_lookup(features, key); 49 50 g_print("%s=", key); 51 switch (G_VALUE_TYPE(val)) { 52 case G_TYPE_STRING: 53 g_print("%s/n", g_value_get_string(val)); 54 break; 55 case G_TYPE_INT: 56 g_print("%d/n", g_value_get_int(val)); 57 break; 58 default: 59 g_print("Value is of unmanaged type!/n"); 60 } 61 } 62 63 g_list_free(keys); 64 } 65 66 void sms_release_features(GHashTable *features) 67 { 68 g_hash_table_destroy(features); 69 } 70
Sms_get_features_type call dbus_g_type_get_map to create a {SV} type. The server is used to create signals. The client will use the call method and registration signal. Sms_create_features calls g_hash_table_new_full to create a hash table, and registers the clearing function of the value object at the same time. When sms_release_features calls g_hash_table_destroy to destroy the hash table, the value object cleanup function registered during creation is called.
5. Client 5.1, code
The client program is as follows:
$ cat -n smsc.c 1 #include <dbus/dbus-glib.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <glib/giochannel.h> 6 #include "sms-marshal.h" 7 #include "sms_features.h" 8 9 #define SMSC_DEBUG 10 11 static void lose (const char *str, ...) 12 { 13 va_list args; 14 va_start (args, str); 15 vfprintf (stderr, str, args); 16 fputc ('/n', stderr); 17 va_end (args); 18 exit (1); 19 } 20 21 static void lose_gerror (const char *prefix, GError *error) 22 { 23 if (error) { 24 lose ("%s: %s", prefix, error->message); 25 } 26 else { 27 lose ("%s", prefix); 28 } 29 } 30 31 static void incoming_message_handler (DBusGProxy *proxy, const char *address, const char *contents, GHashTable *features, gpointer user_data) 32 { 33 printf ("Received message with addree /"%s/" and it says: /n%s/n", address, contents); 34 sms_show_features(features); 35 } 36 37 static void send_message(DBusGProxy *remote_object) 38 { 39 GError *error = NULL; 40 GHashTable *features; 41 int ret; 42 43 features = sms_create_features ("gsm", 8, 2); 44 printf("SendMessage "); 45 46 if (!dbus_g_proxy_call (remote_object, "SendMessage", &error, 47 G_TYPE_STRING, "10987654321", G_TYPE_STRING, "hello world", 48 sms_get_features_type(), features, G_TYPE_INVALID, 49 G_TYPE_INT, &ret, G_TYPE_INVALID)) 50 lose_gerror ("Failed to complete SendMessage", error); 51 52 printf("return %d/n", ret); 53 sms_release_features(features); 54 } 55 56 static void shell_help(void) 57 { 58 printf( "/ts/tsend message/n" 59 "/tq/tQuit/n" 60 ); 61 } 62 63 #define STDIN_BUF_SIZE 1024 64 static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data) 65 { 66 int rc; 67 char buf[STDIN_BUF_SIZE+1]; 68 DBusGProxy *remote_object = (DBusGProxy *)data; 69 70 if (condition != G_IO_IN) { 71 return TRUE; 72 } 73 74 /* we've received something on stdin. */ 75 printf("# "); 76 rc = fscanf(stdin, "%s", buf); 77 if (rc <= 0) { 78 printf("NULL/n"); 79 return TRUE; 80 } 81 82 if (!strcmp(buf, "h")) { 83 shell_help(); 84 } else if (!strcmp(buf, "?")) { 85 shell_help(); 86 } else if (!strcmp(buf, "s")) { 87 send_message(remote_object); 88 } else if (!strcmp(buf, "q")) { 89 exit(0); 90 } else { 91 printf("Unknown command `%s'/n", buf); 92 } 93 return TRUE; 94 } 95 96 int main (int argc, char **argv) 97 { 98 DBusGConnection *bus; 99 DBusGProxy *remote_object; 100 GError *error = NULL; 101 GMainLoop *mainloop; 102 GIOChannel *chan; 103 guint source; 104 GType features_type; 105 106 #ifdef SMSC_DEBUG 107 g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE); 108 #endif 109 g_type_init (); 110 mainloop = g_main_loop_new (NULL, FALSE); 111 112 bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); 113 if (!bus) 114 lose_gerror ("Couldn't connect to session bus", error); 115 116 remote_object = dbus_g_proxy_new_for_name (bus, "org.freesmartphone.ogsmd", 117 "/org/freesmartphone/GSM/Device", 118 "org.freesmartphone.GSM.SMS"); 119 if (!remote_object) 120 lose_gerror ("Failed to get name owner", NULL); 121 122 features_type = sms_get_features_type(); 123 dbus_g_object_register_marshaller (sms_marshal_VOID__STRING_STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, 124 features_type, G_TYPE_INVALID); 125 dbus_g_proxy_add_signal (remote_object, "IncomingMessage", G_TYPE_STRING, G_TYPE_STRING, features_type, G_TYPE_INVALID); 126 dbus_g_proxy_connect_signal (remote_object, "IncomingMessage", G_CALLBACK (incoming_message_handler), NULL, NULL); 127 128 chan = g_io_channel_unix_new(0); 129 source = g_io_add_watch(chan, G_IO_IN, channel_cb, remote_object); 130 g_main_loop_run (mainloop); 131 exit (0); 132 }
Line 3 connects to the session bus. Row 116-118 gets the connection on the session bus "org. freesmartphone. "org. freesmartphone. GSM. the interface proxy object of SMS.
Row 3 calls dbus_g_object_register_marshaller to register the column set function with feature-glib. Line 3 calls dbus_g_proxy_add_signal to add a listener for incomingmessage. The callback function of incomingmessage. Row 123 registers the sms_external_void _ string_string_boxed function generated using glib-genequalal. Callback-glib uses this function to retrieve signal parameters from the signal message and pass them to the callback function, that is, to execute the scattered set operation. This indicates that the column set function generated by glib-genstmal can be used for both the column set and the scattered set.
The client program also uses the IO channel to accept user input. When registering the callback function, Row 3 imports a pointer to the interface proxy object as a parameter. The callback function channel_cb calls the sendmessage method of the org. freesmartphone. GSM. SMS interface object through the send_message function after you type the 's' command. The g_slice_config_always_malloc setting in row 107 is also used to check memory leakage with valgrind.
5.2 execution
Run container-monitor first, then SMSs, and then SMSC. First, type 's' in SMSC and press enter to call the sendmessage method. Then, type 's' in SMSs and press enter to send the incomingmessage signal. Then, type 'q' in SMSC and press enter to exit. Finally, type 'q' in SMSs and press enter to exit.
$ ./smssservice is runningnumber=10987654321contents=hello worldcsm_num=8alphabet=gsmcsm_seq=2h# s send signal q Quits# q
$ ./smsch# s send message q Quits# SendMessage return 11Received message with addree "12345678901" and it says: hello signal!csm_num=3alphabet=ucs2csm_seq=1q
We can see the printed signal and message. For the same thing, the observer at different levels will see different details. The following table shows what the observer-monitor sees:
SMSs connects to the session bus. The session bus sends the nameownerchanged signal, and the unique Notification name ": 1.21" is allocated. |
Signal sender = org. freedesktop. Large-> DEST = (null destination) Path =/org/freedesktop/large; interface = org. freedesktop. Large; member = nameownerchanged String ": 1.21" String "" String ": 1.21" |
SMSs sends hello to the session bus to get its unique name ": 1.21 ". |
Method call sender =: 1.21-> DEST = org. freedesktop. Export Path =/org/freedesktop/export; interface = org. freedesktop. Submit; member = Hello |
SMSs calls addmatch to receive the nameownerchanged signal of the session bus. |
Method call sender =: 1.21-> DEST = org. freedesktop. Export Path =/org/freedesktop/plugin; interface = org. freedesktop. plugin; member = addmatch String "type = 'signal ', sender = 'org. freedesktop. export ', Path ='/org/freedesktop/export ', interface = 'org. freedesktop. comment ', member = 'nameownerchanged '" |
SMSs calls addmatch to receive all signals sent by the session bus. |
Method call sender =: 1.21-> DEST = org. freedesktop. Export Path =/org/freedesktop/plugin; interface = org. freedesktop. plugin; member = addmatch String "type = 'signal ', sender = 'org. freedesktop. login', Path ='/', interface = 'org. freedesktop. login '" |
SMSs calls getnameowner to obtain the unique name of the Connection "org. freedesktop. connector. |
Method call sender =: 1.21-> DEST = org. freedesktop. Export Path =/org/freedesktop/partition; interface = org. freedesktop. Producer; member = getnameowner String "org. freedesktop. Large" |
The session bus sends the nameownerchanged signal, and the only connection with the name ": 1.21" is notified to get the public name "org. freesmartphone. ogsmd ". |
Signal sender = org. freedesktop. Large-> DEST = (null destination) Path =/org/freedesktop/large; interface = org. freedesktop. Large; member = nameownerchanged String "org. freesmartphone. ogsmd" String "" String ": 1.21" |
SMSs request public name "org. freesmartphone. ogsmd ". Before the public name allocation, the request for public name should be followed by the monitoring process reversing the message order. |
Method call sender =: 1.21-> DEST = org. freedesktop. Export Path =/; interface = org. freedesktop. Submit; member = requestname String "org. freesmartphone. ogsmd" Uint32 0 |
SMSC connects to the session bus. The session bus sends the nameownerchanged signal, and the unique Notification name ": 1.22" is allocated. |
Signal sender = org. freedesktop. Large-> DEST = (null destination) Path =/org/freedesktop/large; interface = org. freedesktop. Large; member = nameownerchanged String ": 1.22" String "" String ": 1.22" |
SMSs sends hello to the session bus to get its unique name ": 1.22 ". |
Method call sender =: 1.22-> DEST = org. freedesktop. Export Path =/org/freedesktop/export; interface = org. freedesktop. Submit; member = Hello |
SMSC calls addmatch to receive the nameownerchanged signal of the session bus. |
Method call sender =: 1.22-> DEST = org. freedesktop. Export Path =/org/freedesktop/plugin; interface = org. freedesktop. plugin; member = addmatch String "type = 'signal ', sender = 'org. freedesktop. export ', Path ='/org/freedesktop/export ', interface = 'org. freedesktop. comment ', member = 'nameownerchanged '" |
SMSC calls addmatch to receive signals from the 'org. freesmartphone. ogsmd 'interface of the object'/org/freesmartphone/GSM/devic' in the 'org. freesmartphone. msms 'connection. |
Method call sender =: 1.22-> DEST = org. freedesktop. Export Path =/org/freedesktop/plugin; interface = org. freedesktop. plugin; member = addmatch String "type = 'signal ', sender = 'org. freesmartphone. ogsmd ', Path ='/org/freesmartphone/GSM/devic', interface = 'org. freesmartphone. GSM. SMS '" |
SMSC calls getnameowner to obtain the unique name of the Connection "org. freesmartphone. ogsmd. |
Method call sender =: 1.22-> DEST = org. freedesktop. Export Path =/org/freedesktop/partition; interface = org. freedesktop. Producer; member = getnameowner String "org. freesmartphone. ogsmd" |
SMSC calls the sendmessage method of the 'org. freesmartphone. message' interface of the object '/org/freesmartphone/GSM/devic' in the connection 'org. freesmartphone. ogsmd. |
Method call sender =: 1.22-> DEST = org. freesmartphone. ogsmd Path =/org/freesmartphone/GSM/device; interface = org. freesmartphone. GSM. SMS; member = sendmessage String "10987654321" String "Hello World" Array [ Dict entry ( String "csm_seq" Variant int32 2 ) Dict entry ( String "Alphabet" Variant string "GSM" ) Dict entry ( String "csm_num" Variant int32 8 ) ] |
SMSs sends the method return message to SMSC and returns the output parameters of the sendmessage method. |
Method return sender =: 1.21-> DEST =: 1.22 reply_serial = 5 Int32 11 |
SMSs sends the incomingmessage signal. |
Signal sender =: 1.21-> DEST = (null destination) Path =/org/freesmartphone/GSM/device; interface = org. freesmartphone. GSM. SMS; member = incomingmessage String "12345678901" String "Hello signal! " Array [ Dict entry ( String "csm_seq" Variant int32 1 ) Dict entry ( String "Alphabet" Variant string "ucs2" ) Dict entry ( String "csm_num" Variant int32 3 ) ] |
Session bus notification connection ": 1.22", that is, the SMSC connection has been disconnected. |
Signal sender = org. freedesktop. Large-> DEST = (null destination) Path =/org/freedesktop/large; interface = org. freedesktop. Large; member = nameownerchanged String ": 1.22" String ": 1.22" String "" |
The session bus notifies you that the connection with the public name "org. freesmartphone. ogsmd" has been disconnected. |
Signal sender = org. freedesktop. Large-> DEST = (null destination) Path =/org/freedesktop/large; interface = org. freedesktop. Large; member = nameownerchanged String "org. freesmartphone. ogsmd" String ": 1.21" String "" |
The connection with the unique name ": 1.21" of the session bus notification has been disconnected. That is, the SMSs has been terminated. |
Signal sender = org. freedesktop. Large-> DEST = (null destination) Path =/org/freedesktop/large; interface = org. freedesktop. Large; member = nameownerchanged String ": 1.21" String ": 1.21" String "" |
6. Engineering
The downloaded files are created using make distcheck, which contains automatically generated files. Execute./clean. Sh to delete the automatically generated files, leaving only the files I created:
$ find . -type f./clean.sh./Makefile.am./autogen.sh./src/gsm_sms.h./src/Makefile.am./src/sms-marshal.list./src/smss.xml./src/smss.c./src/gsm_sms.c./src/sms_features.h./src/sms_features.c./src/smsc.c./configure.ac
All source files have been described earlier. Let's take a look at the project file:
$ cat autogen.sh#! /bin/shtouch `find .`aclocalautoconfautoheadertouch NEWS README AUTHORS ChangeLogautomake --add-missing$ cat Makefile.amSUBDIRS = srcEXTRA_DIST = autogen.sh clean.sh
Autogen. Sh creates a project environment. After clean. Sh is executed, execute autogen. Sh to regenerate project files such as configure. The touch command is used to prevent the file from having a future timestamp. This type of problem may occur when I run Ubuntu on a virtual machine. Makefile. am also uses autogen. Sh clean. Sh as the release file. The most important project files are "Configure. ac" and "src/makefile. Am ".
6.1. Configure. AC
$ cat -n configure.ac 1 AC_INIT() 2 AM_INIT_AUTOMAKE(hello-dbus5, 0.1) 3 AM_CONFIG_HEADER(config.h) 4 5 AC_PROG_CC 6 7 8 # Dbus detection 9 PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no) 10 11 if test x$have_dbus = xno ; then 12 AC_MSG_ERROR([DBus development libraries not found]) 13 fi 14 AM_CONDITIONAL(HAVE_DBUS, test x$have_dbus = xyes) 15 16 AC_SUBST(DBUS_CFLAGS) 17 AC_SUBST(DBUS_LIBS) 18 19 20 # Glib detection 21 PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0 >= 2.6, have_glib=yes, have_glib=no) 22 23 if test x$have_glib = xno ; then 24 AC_MSG_ERROR([GLib development libraries not found]) 25 fi 26 27 AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes) 28 29 AC_SUBST(DBUS_GLIB_CFLAGS) 30 AC_SUBST(DBUS_GLIB_LIBS) 31 32 33 AC_OUTPUT([Makefile 34 src/Makefile])
Line 8-17 checks the inventory library, which will generate the compilation constants dbus_cflags and dbus_libs. Check the runtime-glib library in rows 20-30. They will generate the compilation constants dbus_glib_cflags and dbus_glib_libs.
6.2. src/makefile. AM
$ cat -n src/Makefile.am 1 INCLUDES = / 2 $(DBUS_CFLAGS) / 3 $(DBUS_GLIB_CFLAGS) / 4 -DDBUS_COMPILATION 5 6 LIBS = / 7 $(DBUS_LIBS) / 8 $(DBUS_GLIB_LIBS) / 9 -ldbus-glib-1 10 11 # smss 12 noinst_PROGRAMS = smss 13 14 BUILT_SOURCES = smss-glue.h sms-marshal.h sms-marshal.c 15 smss_SOURCES = $(BUILT_SOURCES) smss.c gsm_sms.c sms_features.c 16 noinst_HEADERS = gsm_sms.h sms_features.h 17 18 19 smss-glue.h: smss.xml 20 $(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml 21 22 sms-marshal.h: sms-marshal.list 23 $(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h 24 25 sms-marshal.c: sms-marshal.list 26 $(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c 27 28 CLEANFILES = $(BUILT_SOURCES) 29 30 EXTRA_DIST = smss.xml sms-marshal.list 31 32 # smss 33 noinst_PROGRAMS += smsc 34 smsc_SOURCES= smsc.c sms-marshal.c sms_features.c
Line 19-20 The smss-glue.h of the stub file is generated by the interface description file SMSs. xml. 22-26 rows are defined by the column set interface to generate code containing the column set function.
7. Conclusion
This article describes a simple example of secure-glib, including servers and clients. In the first lecture, there is an addition example. If you understand the example in this article, the example is simpler. The producer-glib Source Code contains two examples:
- Example-service and example-client demonstrate method call. In this example, the interface description file contains an error in parameter type. If (US) is written as (SS), an error occurs during running. The author may want to demonstrate the consequences of inconsistent interface definitions with code implementations. You can download the modified Code from here.
- Example-signal-emitter and example-signal-recipient demonstrate signal transmission. In this example, example-signal-recipient calls the example-signal-emitter method to send signals. In fact, the signal should be from the server side. I changed it to typing in example-signal-emitter to send a signal. You can download the modified Code from here.
Now, let's see how to explain the renewal instance. In fact, all my articles only hope to make the complex world a little simpler.