XmlBlaster Logo

REQUIREMENT

client.c.socket

XmlBlaster Logo


Type NEW
Priority HIGH
Status CLOSED
Topic XmlBlaster provides a client library written in ANSI C
Des
cription

For C client applications you can use the header XmlBlasterAccessUnparsed.h which provides access to xmlBlaster in an easy way.

Features

  • Support on many OS like MAC OSX, iPhone, Windows, Windows-CE / Pocket PC, Sun-UNIX, HPUX, Linux and the code is easy portable to embedded devices
  • Compiles with C or with C++ compiler
  • The communication protocol is based on the xmlBlaster internal SOCKET protocol
  • Object/executable size far below 100 KBytes
  • Supports synchronous xmlBlaster access with connect(), disconnect(), get(), publish(), erase(), ping()
    See example: XmlBlasterConnectionUnparsedMain.c
  • Supports asynchronous access with client callback update()
    See example: CallbackServerUnparsed.c
  • Supports sync and async communications with pthreads.
    Tested on Linux and Solaris and Windows, see example XmlBlasterAccessUnparsed.c
    Uses posix threads DLL for Win, PocketPC, WinCE from http://sources.redhat.com/pthreads-win32
    This LGPL lib is included in our distribution xmlBlaster/src/c/socket/pthreadVC2.lib
  • Reports exceptions nicely back to the client
  • The client side xmlBlasterAccessUnparsed structure has function pointers for all methods to have object oriented like access, see HelloWorld.c as a usage example
  • You can create multiple independent instances of client connections or callback servers and they can run in parallel.
  • The access library and the callback library is thread safe (no global variables).
  • The callback messages reuse the synchronous socket connection to tunnel messages back to the client. This makes possible a simple fire wall tunneling.
  • For lower net latency you can switch to UDP based communication.
  • High efficient compression on socket stream layer is supported with zlib.
  • The code is tested for memory leaks with valgrind and partially statically checked with splint.

Functionality

All invocations to xmlBlaster are done by invoking function pointers on struct XmlBlasterAccessUnparsed.

Memory footprint

On Linux the 'hello world' client executable has a very little footprint, on Windows you need to add the pthreadVC2.lib size of ~ 29 kBytes:

1. Complete synchronous access (without callback and single threaded):

 XmlBlasterConnectionUnparsedMain:  30 kBytes

   libc.so.6 => /lib/libc.so.6 (0x4002b000)
   /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

2. Complete client connection and multi threaded callback server:

 XmlBlasterAccessUnparsedMain:      50 kBytes

   libpthread.so.0 => /lib/libpthread.so.0 (0x4002b000)
   libc.so.6 => /lib/libc.so.6 (0x4007b000)
   /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

3. Size of the C client shared library:

   xmlBlaster/lib/libxmlBlasterClientC.so:  48 kBytes

4. Size of a multi threaded C client (using the above shared library)
   
  LogRedirect:    6,3 kBytes 

   libxmlBlasterClientC.so => 
      /opt/xmlBlaster/lib/libxmlBlasterClientC.so (0x40016000)
   libdl.so.2 => /lib/libdl.so.2 (0x40037000)
   libpthread.so.0 => /lib/libpthread.so.0 (0x4003a000)
   libc.so.6 => /lib/libc.so.6 (0x4008a000)
   /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

     

Compilation with ant

You can try the ant based compilation, our build.xml supports Linux GNU gcc, Linux Intel icc, Solaris CC and Windows VC++:

cd xmlBlaster
build              c-lib     (UNIX gcc)
build -Duse-gcc=1  c-lib     (Mac OSX with gcc)
build -Duse-icc=1  c-lib     (UNIX Intel icc compiler)
build -Duse-CC=1   c-lib     (Solaris CC)
build -Duse-msvc=1 c-lib     (Windows Visual C++)
     

This creates a shared C library xmlBlaster/lib/libxmlBlasterClientC.so on Linux or xmlBlaster/lib/xmlBlasterClientC.dll on Windows. You can link your C or C++ client code with this library.

You can set your preferred compiler in the file xmlBlaster/build.properties to avoid setting it on command line always again:

use-msvc = 1
     

To compile the C client library and the demos in xmlBlaster/demo/c/socket invoke (please use the above compiler names if necessary):

cd xmlBlaster
build c
     

Currently debugging support in the executable is switched on as default, to switch it off try (the -verbose option is to dump the compilation command line only):

build -Dc.debug=false -verbose c
     

You can force using the INTEL C++ compiler by setting use-icc to true:

build -Duse-icc=true c
     

Compilation manually

Here are some complete examples how to compile a simple C client 'manually':

cd xmlBlaster/src/c

Linux C: (for Mac OSX add '-D__MacOSX__')
  gcc -Wall -g -I. -UXB_USE_PTHREADS -o HelloWorld HelloWorld.c util/helper.c util/msgUtil.c util/Properties.c
      socket/xmlBlasterSocket.c socket/XmlBlasterConnectionUnparsed.c socket/xmlBlasterZlib.c

Linux C++:
  g++ -Wall -g -DXMLBLASTER_C_COMPILE_AS_CPP -I. -o HelloWorld HelloWorld.c
       util/*.c socket/XmlBlasterConnectionUnparsed.c socket/xmlBlasterSocket.c socket/xmlBlasterZlib.c

Windows:
  cl -D_WINDOWS -DDLL_IGNORE -DXB_NO_PTHREADS -I. HelloWorld.c util\*.c socket\XmlBlasterConnectionUnparsed.c
     socket\xmlBlasterSocket.c socket\xmlBlasterZlib.c ws2_32.lib

Solaris:
  cc -Xc -g -I. -o HelloWorld HelloWorld.c util/*.c
   socket/XmlBlasterConnectionUnparsed.c socket/xmlBlasterSocket.c -lsocket -lnsl
     

In the first example we undef XB_USE_PTHREADS to have no thread ID in the logging output, this switch is optional. In the second example we use the C++ compiler instead of the C compiler and force with XMLBLASTER_C_COMPILE_AS_CPP the code to not include extern "C" { ... } constructs, this way we get a pure C++ binary instead of an extern C module. This switch is optional as well.

To compile a multi threaded client with synchronous access and asynchronous callbacks:

cd xmlBlaster/src/c/socket

Linux C:
  gcc -DXmlBlasterAccessUnparsedMain -Wall -pedantic -g -D_REENTRANT -I..
    -o XmlBlasterAccessUnparsedMain XmlBlasterAccessUnparsed.c ../util/*.c
    xmlBlasterSocket.c XmlBlasterConnectionUnparsed.c CallbackServerUnparsed.c
    -lpthread

Windows:
   cd xmlBlaster\src\c\socket
   cl /MT /W4 -DDLL_IGNORE -DXmlBlasterAccessUnparsedMain -D_WINDOWS
      -I.. -I../pthreads /FeXmlBlasterAccessUnparsedMain.exe  XmlBlasterAccessUnparsed.c ..\util\*.c
      xmlBlasterSocket.c XmlBlasterConnectionUnparsed.c CallbackServerUnparsed.c xmlBlasterZlib.c
      ws2_32.lib pthreadVC2.lib

Windows (including zlib compression):
   cl /MT /W4 -DDLL_IGNORE -DXmlBlasterAccessUnparsedMain -D_WINDOWS /DXMLBLASTER_ZLIB=1 \
      -I.. -I../pthreads  /FeXmlBlasterAccessUnparsedMain.exe \
      XmlBlasterAccessUnparsed.c ..\util\*.c *.c  ws2_32.lib pthreadVC2.lib \zlib121\lib\zdll.lib
    
Solaris:
  cc  -DXmlBlasterAccessUnparsedMain -v -Xc -g -D_REENTRANT -I..
    -o XmlBlasterAccessUnparsedMain XmlBlasterAccessUnparsed.c ../util/*.c
    xmlBlasterSocket.c XmlBlasterConnectionUnparsed.c
    CallbackServerUnparsed.c -lpthread -lsocket -lnsl
     

Compilation hints are in the source files xmlBlaster/src/c/XmlBlasterConnectionUnparsedMain.c (for synchronous access) xmlBlaster/src/c/socket/XmlBlasterAccessUnparsed.c (for complete multi threaded access including callbacks).

Implementation detail

The C library consists of 3 independent modules which are XmlBlasterConnectionUnparsed for synchronous access, CallbackServerUnparsed for the client callbacks and XmlBlasterAccessUnparsed which reuses the first two modules and combines them with multi threading to gain complete xmlBlaster functionality. The following sequence diagram shows a use case of a get() invocation, the threads are synchronized using the worker/boss paradigm with the wait/signal approach of pthreads.

Client C Sequence Diagram

A demo using XmlRpc is available as well (see xmlBlaster/demo/c/xmlrpc/README).

Example
C

Example for a Windows XP setup

Here is an example how to setup the development environment for free on Windows, using our ant build to compile.

Compilation steps on a Windows (XP,2003) platform (using free microsoft tools only):

  1. Download Visual C++ 2005 Express Edition and install it
  2. Download Microsoft SDK and install it
  3. Configure VC++ express as documented. This is optional as we compile the dll and demo clients with ant
  4. Configure your command prompt environment to find the SDK and the C++ compiler:
    call "C:\Program Files\Microsoft Platform SDK\SetEnv.Cmd"
    call "C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat"
    Or set manually in your command shell:
    set INCLUDE=C:\Program Files\Microsoft Platform SDK\Include;%INCLUDE%
    set LIB=C:\Program Files\Microsoft Platform SDK\Lib;%LIB%
    set Path=C:\Program Files\Microsoft Platform SDK\Bin;%PATH%
  5. Compile the C library with build c

Steps to run the executable:

  1. Configure the environment:
    REM to find xmlBlasterClientCD.dll:
    set PATH=%PATH%;\xmlBlaster\lib
    
    REM to find HelloWorld3.exe:
    set PATH=%PATH%;\xmlBlaster\demo\c\socket\bin
  2. Run the demo
    HelloWorld3

If you want to use compressed communication, please download zlib from http://www.zlib.net/ and configure it in build.properties

Note for C++: You need to download the xerces XML parser http://xerces.apache.org and configure it in build.properties, after that call build cpp to compile everything. If you want to use client side queuing download sqlitedll-2_8_17.zip from http://www.sqlite.org and configure its usage in build.properties.

Example
C

Single threaded example

This example shows a single threaded synchronous access:


 1 /*----------------------------------------------------------------------------
 2 Name:      HelloWorld.c
 3 Project:   xmlBlaster.org
 4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
 5 Comment:   HelloWorld connects with raw socket to xmlBlaster
 6 Author:    "Marcel Ruff" <xmlBlaster@marcelruff.info>
 7 Compile:
 8   Linux C: gcc -Wall -g -D_REENTRANT -I. -o HelloWorld HelloWorld.c util/msgUtil.c
 9    util/Properties.c socket/xmlBlasterSocket.c socket/XmlBlasterConnectionUnparsed.c
10   Linux C++: g++ -Wall -g -D_REENTRANT -I. -o HelloWorld HelloWorld.c util/msgUtil.c
11    util/Properties.c socket/xmlBlasterSocket.c socket/XmlBlasterConnectionUnparsed.c
12             -DXMLBLASTER_C_COMPILE_AS_CPP
13   Win:  cl /MT /W3 /Wp64 -D_WINDOWS -I. HelloWorld.c util\*.c socket\*.c ws2_32.lib
14   Sun:  cc -g -D_REENTRANT -I. -o HelloWorld HelloWorld.c util/msgUtil.c
15         util/Properties.c socket/xmlBlasterSocket.c
16         socket/XmlBlasterConnectionUnparsed.c -lsocket -lnsl
17 
18   Linux with shared lib:
19         gcc -o HelloWorld HelloWorld.c -L../../lib -lxmlBlasterClientC -I.
20             -Wl,-rpath=../../lib -lpthread
21 Date:      05/2003
22 -----------------------------------------------------------------------------*/
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <XmlBlasterConnectionUnparsed.h>
27 
28 /**
29  * Access the free memory in the server. 
30  */
31 int main(int argc, char** argv)
32 {
33    MsgUnitArr *msgUnitArr;
34    XmlBlasterException exception;
35    char *connectQos, *response;
36    
37    XmlBlasterConnectionUnparsed *xb = getXmlBlasterConnectionUnparsed(argc, argv);
38 
39    connectQos =   "<qos>"
40                   " <securityService type='htpasswd' version='1.0'>"
41                   "   <user>fritz</user>"
42                   "   <passwd>secret</passwd>"
43                   " </securityService>"
44                   "</qos>";
45    response = xb->connect(xb, connectQos, &exception);
46    xmlBlasterFree(response);
47    if (*exception.errorCode != '\0') {
48       printf("[client] Caught exception during connect, errorCode=%s, message=%s",
49              exception.errorCode, exception.message);
50       freeXmlBlasterConnectionUnparsed(xb);
51       exit(1);
52    }
53 
54    printf("[HelloWorld] Connected to xmlBlaster, invoking now get() ...\n");
55    
56    msgUnitArr = xb->get(xb, "<key oid='__cmd:?freeMem'/>", 0, &exception);
57    if (*exception.errorCode != '\0') {
58       printf("[HelloWorld] Caught exception in get errorCode=%s, message=%s",
59              exception.errorCode, exception.message);
60       freeXmlBlasterConnectionUnparsed(xb);
61       exit(1);
62    }
63    if (msgUnitArr != (MsgUnitArr *)0 && msgUnitArr->len > 0) {
64       char *contentStr = strFromBlobAlloc(msgUnitArr->msgUnitArr[0].content,
65                                           msgUnitArr->msgUnitArr[0].contentLen);
66       printf("[HelloWorld] xmlBlaster has %s bytes of free memory\n", contentStr);
67       xmlBlasterFree(contentStr);
68    }
69    freeMsgUnitArr(msgUnitArr);
70    
71    (void)xb->disconnect(xb, 0, &exception);
72 
73    freeXmlBlasterConnectionUnparsed(xb);
74    printf("[HelloWorld] Good bye.\n");
75    return 0;
76 }

We include the header XmlBlasterConnectionUnparsed.h on line 26 for synchronous access. This header contains a struct with function pointers on all functions necessary to communicate with xmlBlaster.

On line 37 we create a client connection instance (one instance of the above struct), which allows us to connect to xmlBaster and work with the server. If we need multiple connections we just call getXmlBlasterConnectionUnparsed(argc, argv) multiple times which returns totally independent connection handlers which may be used from different threads if desired.

After every server invocation (e.g. line 47) we need to check the exception struct. If the errorCode member is filled an exception occurred. The exception source may be from the remote server or from the C library itself.

If we are done with our work we need to free all resources (line 73). This assures we have no memory leak even on a C client running 365 days a year without interruption.

Example
C

Multi threaded example

Here is an example showing multi threaded access with asynchronous callbacks:


  1 /*----------------------------------------------------------------------------
  2 Name:      xmlBlaster/demo/c/socket/HelloWorld3.c
  3 Project:   xmlBlaster.org
  4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5 Comment:   Example for all remote method invocations.
  6 Author:    "Marcel Ruff" <xmlBlaster@marcelruff.info>
  7 Compile:   cd xmlBlaster; build c
  8            (Win: copy xmlBlaster\src\c\socket\pthreadVC2.dll to your PATH)
  9 Invoke:    HelloWorld3 -help
 10 See:    http://www.xmlblaster.org/xmlBlaster/doc/requirements/protocol.socket.html
 11 -----------------------------------------------------------------------------*/
 12 #include <stdio.h>
 13 #include <stdlib.h>
 14 #include <string.h>
 15 #include <XmlBlasterAccessUnparsed.h>
 16 
 17 /**
 18  * Here we receive the callback messages from xmlBlaster
 19  * @see UpdateFp in CallbackServerUnparsed.h
 20  */
 21 static bool myUpdate(MsgUnitArr *msgUnitArr, void *userData,
 22                      XmlBlasterException *exception)
 23 {
 24    size_t i;
 25    bool testException = false;
 26    /* XmlBlasterAccessUnparsed *xa = (XmlBlasterAccessUnparsed *)userData; */
 27 
 28    for (i=0; i<msgUnitArr->len; i++) {
 29       char *xml = messageUnitToXml(&msgUnitArr->msgUnitArr[i]);
 30       printf("[client] CALLBACK update(): Asynchronous message update arrived:%s\n",
 31              xml);
 32       xmlBlasterFree(xml);
 33       msgUnitArr->msgUnitArr[i].responseQos = 
 34                   strcpyAlloc("<qos><state id='OK'/></qos>");
 35       /* Return QoS: Everything is OK */
 36    }
 37    if (testException) {
 38       strncpy0(exception->errorCode, "user.clientCode",
 39                XMLBLASTEREXCEPTION_ERRORCODE_LEN);
 40       strncpy0(exception->message, "I don't want these messages",
 41                XMLBLASTEREXCEPTION_MESSAGE_LEN);
 42       return false;
 43    }
 44    return true;
 45 }
 46 
 47 /**
 48  * Invoke: HelloWorld3 -logLevel TRACE
 49  */
 50 int main(int argc, char** argv)
 51 {
 52    int iarg;
 53    char *response = (char *)0;
 54    /*
 55       * callbackSessionId:
 56       * Is created by the client and used to validate callback messages in update. 
 57       * This is sent on connect in ConnectQos.
 58       * (Is different from the xmlBlaster secret session ID)
 59       */
 60    const char *callbackSessionId = "topSecret";
 61    XmlBlasterException xmlBlasterException;
 62    XmlBlasterAccessUnparsed *xa = 0;
 63 
 64    printf("[client] XmlBlaster %s C SOCKET client, try option '-help' if you need"
 65           " usage informations\n", getXmlBlasterVersion());
 66 
 67    for (iarg=0; iarg < argc; iarg++) {
 68       if (strcmp(argv[iarg], "-help") == 0 || strcmp(argv[iarg], "--help") == 0) {
 69          char usage[XMLBLASTER_MAX_USAGE_LEN];
 70          const char *pp =
 71          "\n  -logLevel            ERROR | WARN | INFO | TRACE [WARN]"
 72          "\n\nExample:"
 73          "\n  HelloWorld3 -logLevel TRACE"
 74          " -dispatch/connection/plugin/socket/hostname 192.168.2.9";
 75          printf("Usage:\nXmlBlaster C SOCKET client %s\n%s%s\n",
 76                   getXmlBlasterVersion(), xmlBlasterAccessUnparsedUsage(usage), pp);
 77          exit(EXIT_FAILURE);
 78       }
 79    }
 80 
 81    xa = getXmlBlasterAccessUnparsed(argc, argv);
 82    if (xa->initialize(xa, myUpdate, &xmlBlasterException) == false) {
 83       printf("[client] Connection to xmlBlaster failed,"
 84              " please start the server or check your configuration\n");
 85       freeXmlBlasterAccessUnparsed(xa);
 86       exit(EXIT_FAILURE);
 87    }
 88 
 89    {  /* connect */
 90       char connectQos[2048];
 91       char callbackQos[1024];
 92       sprintf(callbackQos,
 93                "<queue relating='callback' maxEntries='100' maxEntriesCache='100'>"
 94                "  <callback type='SOCKET' sessionId='%s'>"
 95                "    socket://%.120s:%d"
 96                "  </callback>"
 97                "</queue>",
 98                callbackSessionId, xa->callbackP->hostCB, xa->callbackP->portCB);
 99       sprintf(connectQos,
100                "<qos>"
101                " <securityService type='htpasswd' version='1.0'>"
102                "  <![CDATA["
103                "   <user>fritz</user>"
104                "   <passwd>secret</passwd>"
105                "  ]]>"
106                " </securityService>"
107                "%.1024s"
108                "</qos>", callbackQos);
109 
110       response = xa->connect(xa, connectQos, myUpdate, &xmlBlasterException);
111       if (*xmlBlasterException.errorCode != 0) {
112          printf("[client] Caught exception during connect errorCode=%s, message=%s\n",
113                   xmlBlasterException.errorCode, xmlBlasterException.message);
114          freeXmlBlasterAccessUnparsed(xa);
115          exit(EXIT_FAILURE);
116       }
117       xmlBlasterFree(response);
118       printf("[client] Connected to xmlBlaster, do some tests ...\n");
119    }
120 
121    response = xa->ping(xa, 0);
122    if (response == (char *)0) {
123       printf("[client] ERROR: Pinging a connected server failed\n");
124    }
125    else {
126       printf("[client] Pinging a connected server, response=%s\n", response);
127       xmlBlasterFree(response);
128    }
129 
130    { /* subscribe ... */
131       const char *key = "<key oid='HelloWorld'/>";
132       const char *qos = "<qos/>";
133       printf("[client] Subscribe message 'HelloWorld' ...\n");
134       response = xa->subscribe(xa, key, qos, &xmlBlasterException);
135       if (*xmlBlasterException.errorCode != 0) {
136          printf("[client] Caught exception in subscribe errorCode=%s, message=%s\n",
137                   xmlBlasterException.errorCode, xmlBlasterException.message);
138          xa->disconnect(xa, 0, &xmlBlasterException);
139          freeXmlBlasterAccessUnparsed(xa);
140          exit(EXIT_FAILURE);
141       }
142       printf("[client] Subscribe success, returned status is '%s'\n", response);
143       xmlBlasterFree(response);
144    }
145 
146    {  /* publish ... */
147       MsgUnit msgUnit;
148       printf("[client] Publishing message 'HelloWorld' ...\n");
149       msgUnit.key = strcpyAlloc("<key oid='HelloWorld'/>");
150       msgUnit.content = strcpyAlloc("Some message payload");
151       msgUnit.contentLen = strlen(msgUnit.content);
152       msgUnit.qos =strcpyAlloc("<qos><persistent/></qos>");
153       response = xa->publish(xa, &msgUnit, &xmlBlasterException);
154       freeMsgUnitData(&msgUnit);
155       if (*xmlBlasterException.errorCode != 0) {
156          printf("[client] Caught exception in publish errorCode=%s, message=%s\n",
157                   xmlBlasterException.errorCode, xmlBlasterException.message);
158          xa->disconnect(xa, 0, &xmlBlasterException);
159          freeXmlBlasterAccessUnparsed(xa);
160          exit(EXIT_FAILURE);
161       }
162       printf("[client] Publish success, returned status is '%s'\n", response);
163       xmlBlasterFree(response);
164    }
165 
166    {  /* unSubscribe ... */
167       const char *key = "<key oid='HelloWorld'/>";
168       const char *qos = "<qos/>";
169       printf("[client] UnSubscribe message 'HelloWorld' ...\n");
170       response = xa->unSubscribe(xa, key, qos, &xmlBlasterException);
171       if (response) {
172          printf("[client] Unsubscribe success, returned status is '%s'\n", response);
173          xmlBlasterFree(response);
174       }
175       else {
176          printf("[client] Caught exception in unSubscribe errorCode=%s, message=%s\n",
177                   xmlBlasterException.errorCode, xmlBlasterException.message);
178          xa->disconnect(xa, 0, &xmlBlasterException);
179          freeXmlBlasterAccessUnparsed(xa);
180          exit(EXIT_FAILURE);
181       }
182    }
183 
184    {  /* get synchnronous ... */
185       size_t i;
186       const char *key = "<key queryType='XPATH'>//key</key>";
187       const char *qos = "<qos/>";
188       MsgUnitArr *msgUnitArr;
189       printf("[client] Get synchronous messages with XPath '//key' ...\n");
190       msgUnitArr = xa->get(xa, key, qos, &xmlBlasterException);
191       if (*xmlBlasterException.errorCode != 0) {
192          printf("[client] Caught exception in get errorCode=%s, message=%s\n",
193                   xmlBlasterException.errorCode, xmlBlasterException.message);
194          xa->disconnect(xa, 0, &xmlBlasterException);
195          freeXmlBlasterAccessUnparsed(xa);
196          exit(EXIT_FAILURE);
197       }
198       if (msgUnitArr != (MsgUnitArr *)0) {
199          for (i=0; i<msgUnitArr->len; i++) {
200             char *contentStr = strFromBlobAlloc(msgUnitArr->msgUnitArr[i].content,
201                                              msgUnitArr->msgUnitArr[i].contentLen);
202             const char *dots = (msgUnitArr->msgUnitArr[i].contentLen > 96) ?
203                                  " ..." : "";
204             printf("\n[client] Received message#%u/%u:\n"
205                      "-------------------------------------"
206                      "%s\n <content>%.100s%s</content>%s\n"
207                      "-------------------------------------\n",
208                      i+1, msgUnitArr->len,
209                      msgUnitArr->msgUnitArr[i].key,
210                      contentStr, dots,
211                      msgUnitArr->msgUnitArr[i].qos);
212             xmlBlasterFree(contentStr);
213          }
214          freeMsgUnitArr(msgUnitArr);
215       }
216       else {
217          printf("[client] Caught exception in get errorCode=%s, message=%s\n",
218                   xmlBlasterException.errorCode, xmlBlasterException.message);
219          xa->disconnect(xa, 0, &xmlBlasterException);
220          freeXmlBlasterAccessUnparsed(xa);
221          exit(EXIT_FAILURE);
222       }
223    }
224 
225 
226    {  /* erase ... */
227       const char *key = "<key oid='HelloWorld'/>";
228       const char *qos = "<qos/>";
229       printf("[client] Erasing message 'HelloWorld' ...\n");
230       response = xa->erase(xa, key, qos, &xmlBlasterException);
231       if (*xmlBlasterException.errorCode != 0) {
232          printf("[client] Caught exception in erase errorCode=%s, message=%s\n",
233                   xmlBlasterException.errorCode, xmlBlasterException.message);
234          xa->disconnect(xa, 0, &xmlBlasterException);
235          freeXmlBlasterAccessUnparsed(xa);
236          exit(EXIT_FAILURE);
237       }
238       printf("[client] Erase success, returned status is '%s'\n", response);
239       xmlBlasterFree(response);
240    }
241 
242    if (xa->disconnect(xa, 0, &xmlBlasterException) == false) {
243       printf("[client] Caught exception in disconnect, errorCode=%s, message=%s\n",
244                xmlBlasterException.errorCode, xmlBlasterException.message);
245       freeXmlBlasterAccessUnparsed(xa);
246       exit(EXIT_FAILURE);
247    }
248 
249    freeXmlBlasterAccessUnparsed(xa);
250    printf("[client] Good bye.\n");
251    return 0;
252 }
   

We include the header XmlBlasterAccessUnparsed.h on line 15 for full blown access. This header contains a struct with function pointers on all functions necessary to communicate with xmlBlaster. The implementation is multi threaded to receive asynchronous callback messages in the function myUpdate() on line 21.

On line 81 we create a client connection instance (one instance of the above struct), which allows us to connect to xmlBaster and work with the server. If we need multiple connections we just call getXmlBlasterAccessUnparsed(argc, argv) multiple times which returns totally independent connection handlers which may be used from different threads if desired.

Have a look at line 82, at the method invocation xa->initialize(xa, myUpdate). We register our function myUpdate() to receive the callbacks from xmlBlaster. If a callback message arrives a second thread will deliver it to our function myUpdate().
The argument userData can be casted to XmlBlasterAccessUnparsed *xa = (XmlBlasterAccessUnparsed *)userData; and can for example be helpful for clients which want to transport client specific data in xa->userObject to the update() method

On line 92 up to 108 we specify a ConnectQos with XML syntax and use this to connect to xmlBlaster. The XML markup is specified in the interface.connect requirement (see links below). Note the <callback> section, here we pass the callback server address which is in our case the local host name and the generated local port of the synchronous connection. This way callbacks are tunneled through the same socket. If we omit the <callback> it will be inserted by XmlBlasterAccessUnparsed and you don't need to call initialize(), directly calling connect() is enough.

The rest is straight forward, we subscribe to a message, we publish a message we retrieve messages in synchronous mode and clean up at the end. Take care to xmlBlasterfree() resources on the way and to check the exception struct after every invocation to avoid memory leaks.

On line 117 we use the method xmlBlasterFree(p) which does directly a free(p) and nothing more. On UNIX you may safely call free(p) directly but on Windows you'll get problems if you link more then one Windows runtime libraries to your executable, every of those will have its own malloc/free implementation and allocating memory in the DLL and freeing this memory in your client code may crash the application.

Note: If the client wishes to store some client specific data in the XmlBlasterAccessUnparsed instance we provide a void * there: xa->userObject.

Example
C

Redirect logging

This example shows how to redirect the logging output from the C lib:


  1 /*----------------------------------------------------------------------------
  2 Name:      xmlBlaster/demo/c/socket/LogRedirect.c
  3 Project:   xmlBlaster.org
  4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5 Comment:   Tests redirect of logging
  6 Author:    "Marcel Ruff" <xmlBlaster@marcelruff.info>
  7 Compile:
  8   Linux with libxmlBlasterC.so:
  9           gcc  -D_REENTRANT -Wall -o LogRedirect LogRedirect.c -I../../../src/c
 10                 -L../../../lib -lxmlBlasterClientC -Wl,-rpath=../../../lib -lpthread
 11 See:      http://www.xmlblaster.org/xmlBlaster/doc/requirements/client.c.socket.html
 12 -----------------------------------------------------------------------------*/
 13 #include <stdio.h>
 14 #include <stdlib.h>
 15 #include <string.h>
 16 #include <stdarg.h>
 17 #include <ctype.h>
 18 #include <XmlBlasterAccessUnparsed.h>
 19 
 20 static void myLogger(XMLBLASTER_LOG_LEVEL currLevel,
 21                      XMLBLASTER_LOG_LEVEL level,
 22                      const char *location, const char *fmt, ...);
 23 static bool myUpdate(MsgUnitArr *msgUnitArr, void *userData,
 24                      XmlBlasterException *xmlBlasterException);
 25 
 26 /**
 27  * Invoke: LogRedirect -logLevel TRACE
 28  */
 29 int main(int argc, char** argv)
 30 {
 31    int iarg;
 32    char *response = (char *)0;
 33    char connectQos[2048];
 34    XmlBlasterException xmlBlasterException;
 35    XmlBlasterAccessUnparsed *xa = 0;
 36 
 37    printf("[client] Try option '-help' if you need usage informations\n");
 38 
 39    for (iarg=0; iarg < argc; iarg++) {
 40       if (strcmp(argv[iarg], "-help") == 0 || strcmp(argv[iarg], "--help") == 0) {
 41          char usage[XMLBLASTER_MAX_USAGE_LEN];
 42          const char *pp =
 43          "\n  -logLevel            ERROR | WARN | INFO | TRACE [WARN]"
 44          "\n\nExample:"
 45          "\n  LogRedirect -logLevel TRACE"
 46                " -dispatch/connection/plugin/socket/hostname server.mars.universe";
 47          printf("Usage:\nXmlBlaster C SOCKET client %s\n%s%s\n",
 48                   getXmlBlasterVersion(), xmlBlasterAccessUnparsedUsage(usage), pp);
 49          exit(EXIT_FAILURE);
 50       }
 51    }
 52 
 53    xa = getXmlBlasterAccessUnparsed(argc, argv);
 54 
 55    /* Register our own logging function */
 56    xa->log = myLogger;
 57 
 58    /* connect */
 59    sprintf(connectQos,
 60             "<qos>"
 61             " <securityService type='htpasswd' version='1.0'>"
 62             "  <![CDATA["
 63             "   <user>fritz</user>"
 64             "   <passwd>secret</passwd>"
 65             "  ]]>"
 66             " </securityService>"
 67             "</qos>");
 68 
 69    response = xa->connect(xa, connectQos, myUpdate, &xmlBlasterException);
 70    if (*xmlBlasterException.errorCode != 0) {
 71       printf("[client] Caught exception during connect errorCode=%s, message=%s\n",
 72                xmlBlasterException.errorCode, xmlBlasterException.message);
 73       freeXmlBlasterAccessUnparsed(xa);
 74       exit(EXIT_FAILURE);
 75    }
 76    xmlBlasterFree(response);
 77    printf("[client] Connected to xmlBlaster, do some tests ...\n");
 78 
 79    /* ping */
 80    response = xa->ping(xa, 0);
 81    if (response == (char *)0) {
 82       printf("[client] ERROR: Pinging a connected server failed\n");
 83    }
 84    else {
 85       printf("[client] Pinging a connected server, response=%s\n", response);
 86       xmlBlasterFree(response);
 87    }
 88 
 89    /* disconnect */
 90    if (xa->disconnect(xa, 0, &xmlBlasterException) == false) {
 91       printf("[client] Caught exception in disconnect, errorCode=%s, message=%s\n",
 92                xmlBlasterException.errorCode, xmlBlasterException.message);
 93       freeXmlBlasterAccessUnparsed(xa);
 94       exit(EXIT_FAILURE);
 95    }
 96 
 97    freeXmlBlasterAccessUnparsed(xa);
 98    printf("[client] Good bye.\n");
 99    return 0;
100 }
101 
102 /**
103  * Customized logging output is handled by this method. 
104  * <p>
105  * We register this function with 
106  * </p>
107  * <pre>
108  * xa->log = myLogger;
109  * </pre>
110  * @param currLevel The actual log level of the client
111  * @param level The level of this log entry
112  * @param location A string describing the code place
113  * @param fmt The formatting string
114  * @param ... Other variables to log, corresponds to 'fmt'
115  * @see xmlBlaster/src/c/msgUtil.c: xmlBlasterDefaultLogging() is the default
116  *      implementation
117  */
118 static void myLogger(XMLBLASTER_LOG_LEVEL currLevel,
119                      XMLBLASTER_LOG_LEVEL level,
120                      const char *location, const char *fmt, ...)
121 {
122    /* Guess we need no more than 200 bytes. */
123    int n, size = 200;
124    char *p = 0;
125    va_list ap;
126 
127    if (level > currLevel) { /* LOG_ERROR, LOG_WARN, LOG_INFO, LOG_TRACE */
128       return;
129    }
130    if ((p = (char *)malloc (size)) == NULL)
131       return;
132 
133    for (;;) {
134       /* Try to print in the allocated space. */
135       va_start(ap, fmt);
136       n = vsnprintf (p, size, fmt, ap);
137       va_end(ap);
138       /* If that worked, print the string to console. */
139       if (n > -1 && n < size) {
140          if (level == LOG_TRACE)
141             printf("%s %s\n", getLogLevelStr(level), p);
142          else
143             printf("{%s-%s-%s} [%s] %s\n",
144                    __DATE__, __TIME__, getLogLevelStr(level), location, p);
145          free(p);
146          return;
147       }
148       /* Else try again with more space. */
149       if (n > -1)    /* glibc 2.1 */
150          size = n+1; /* precisely what is needed */
151       else           /* glibc 2.0 */
152          size *= 2;  /* twice the old size */
153       if ((p = (char *)realloc (p, size)) == NULL) {
154          return;
155       }
156    }
157 }
158 
159 /**
160  * Here we receive the callback messages from xmlBlaster
161  */
162 static bool myUpdate(MsgUnitArr *msgUnitArr, void *userData,
163                      XmlBlasterException *xmlBlasterException)
164 {
165    size_t i;
166    for (i=0; i<msgUnitArr->len; i++) {
167       char *xml = messageUnitToXml(&msgUnitArr->msgUnitArr[i]);
168       printf("[client] CALLBACK update(): Asynchronous message update arrived:%s\n",
169              xml);
170       xmlBlasterFree(xml);
171       msgUnitArr->msgUnitArr[i].responseQos =
172              strcpyAlloc("<qos><state id='OK'/></qos>");
173       /* Return QoS: Everything is OK */
174    }
175    return true;
176 }
    

In this example we want to do logging ourself and define the method myLogger() to do so (line 118).

The important line (55) is xa->log = myLogger where we register myLogger() to receive all logging output. Note that this should be done directly after the getXmlBlasterAccessUnparsed(argc, argv) function call.

Configure

These configurations are tested:

No. OS Compiler xmlBlaster Thread library Protocol library XML library Date Author Comment
1 Linux 2.4.20 g++ and gcc 3.3 0.848+ pthread SOCKET protocol.socket requirement Unparsed access with raw XML strings 2003-05-29 Marcel README
2 Linux 2.4.9 g++ and gcc 2.95.3 0.848+ pthread SOCKET protocol.socket requirement Unparsed access with raw XML strings 2003-05-29 Marcel README
3 SunOS 5.8 sparc cc: Forte Developer 7 C 5.4 2002/03/09 1.RC2 pthread SOCKET protocol.socket requirement Unparsed access with raw XML strings 2004-12-01 Marcel README
4 WindowsXP VC 7 (May 2003) 0.848+ pthreads-win32 SOCKET protocol.socket requirement Expat is not yet integrated 2003-06-03 Marcel README
5 Linux 2.4.20 Intel(R) C++ Compiler for 32-bit applications, Version 7.1 0.848+ pthread SOCKET protocol.socket requirement Expat is not yet integrated 2003-06-26 Marcel README
build -Duse-icc=true c
6 OSF1 V5.1 1885 DEC alpha gcc 3.3.1 0.85f pthread SOCKET protocol.socket requirement Unparsed access with raw XML strings 2003-10-31 Federico GIGLIO README
build c
7 IBM s390 with Linux 2.4.19 gcc 3.2.2 0.9 pthread SOCKET protocol.socket requirement Unparsed access with raw XML strings 2004-04-02 Marcel README
build c
8 SunOS 5.8 sparc gcc (GCC) 3.3.2 1.RC2 pthread SOCKET protocol.socket requirement Unparsed access with raw XML strings 2004-12-01 Marcel build.properties:
XMLBLASTER_PERSISTENT_QUEUE=1
sqlite.include.dir=/opt/sqlite-bin/include
sqlite.lib.dir=/opt/sqlite-bin/lib

cd xmlBlaster; ./build -Duse-gcc=1 c-delete c
9 HP-UX B.11.11 9000/800 gcc (GCC) 3.4.3 1.01 pthread SOCKET protocol.socket requirement Unparsed access with raw XML strings. Created shared library libxmlBlasterClientCD.so only 2005-02-02 Marcel cd xmlBlaster; ./build -Duse-gcc=1 c-delete c-lib
10 Linux 2.6.11 gcc 4.0.0 1.0.3 pthread SOCKET protocol.socket requirement Expat is not yet integrated 2005-05-06 Marcel README
build c
11 Windows XP Home with SDK VC++ 8 express 2005 (beta 2) 1.0.3 pthreads-win32 SOCKET protocol.socket requirement Expat is not yet integrated 2005-05-14 Marcel README
12 Windows XP Professional with SDK (free) Visual C++ 2005 Express Edition (free), Install steps pre1.3 pthreads-win32 SOCKET protocol.socket requirement Expat is not yet integrated 2006-07-29 Marcel README

These parameters allow to configure the C-client on command line or over the environment (with lower priority):

Property Default / Example Description Impl
-dispatch/connection/plugin/socket/hostname localhost Where to find the server yes
-dispatch/connection/plugin/socket/port 7607 The port where xmlBlaster listens yes
-dispatch/connection/plugin/socket/localHostname localhost Force the local host name of the socket connection. Can be useful on multi homed hosts yes
-dispatch/connection/plugin/socket/localPort 9911 Force the local port of the socket connection. Can be useful with firewalls yes
-dispatch/connection/plugin/compress/type - Set to 'zlib:stream' to switch on compression. You need to download zlib from 'http://www.gzip.org/zlib/' and add it for compilation as described in build.properties. The server side SOCKET plugin must have enabled 'zlib:stream' compression as well in xmlBlasterPlugins.xml. yes
-dispatch/connection/plugin/socket/useUdpForOneway false Set to true to switch on UDP datagram mode for publishOneway() calls. The server side SOCKET plugin must have enabled the UDP datagram listener as well. yes
-dispatch/connection/plugin/socket/responseTimeout 60000 (in milliseconds, defaults to 'forever') How long to block on method invocation. Throws a "resource.exhaust" exception on timeout. yes
-dispatch/callback/plugin/socket/multiThreaded true Every callback call is a separate thread (the update() function). Like this you can call methods like publish() or subscribe() from within the update() call.
The sequence of the message arrival in your update() function is still guaranteed: XmlBlaster uses a maximum of one thread for each client to deliver updates. If xmlBlaster sends an array of messages the complete array is delivered. If you want to avoid to receive more than one message per update call adjust this with setting -dispatch/callback/burstMode/maxEntries 1 for this client or generally in the server.
yes
-logLevel WARN Supported levels are ERROR, WARN, INFO and TRACE yes

Testing

The testsuite resides in xmlBlaster/testsuite/src/c to compile it use ant:

cd xmlBlaster
build c-test
         

To run the testsuite invoke

build runTests
build reportShow
        

And go for a cup of coffee as the complete testsuite will run.

How to create the Doxygen documentation

The C code is commented to suit the Doxygen tool from http://www.doxygen.org. You need to install Doxygen and GraphViz as described in their manuals to create the html or pdf documentation and additionally man pages:

cd $XMLBLASTER_HOME/src/c/doc

doxygen Doxyfile
        

Now you can point your browser on $XMLBLASTER_HOME/doc/doxygen/c/html/index.html to view the documentation.

After setting export MANPATH=:$XMLBLASTER_HOME/doc/doxygen/c/man:$XMLBLASTER_HOME/doc/doxygen/c++/man you are ready to read the manual pages with for example man XmlBlasterAccessUnparsed.

NOTE: Configuration parameters are specified on command line (-someValue 17) or in the xmlBlaster.properties file (someValue=17). See requirement "util.property" for details.
Columns named Impl tells you if the feature is implemented.
Columns named Hot tells you if the configuration is changeable in hot operation.

Todo

This release is stable, but we need to:

  • Adding mutex to every function call?
    Currently you can have many connections to xmlBlaster from one client where each connection may be used by a different thread. But running multi threads on one connection instance is currently not synchronized, this design decision is pending. If you have such a case just add your own mutex to the function calls.
  • Adding a thread pool for update() threads.
  • Add another layer which parses the QoS with Expat
  • Add client persistent queue with SQLite. A client side C based persistent queue is implemented already and used in our C++ client library. Embedding this code into the C client library is an open task (see requirement client.c.queue).
  • Integrate Base64 support, for example from WBXML
  • I18N internationalization with gettext tutorial

C++ remark

This C SOCKET client library is used as a SOCKET protocol plugin for our C++ client library. With this C++ client library you get all the nice additional fail safe features and XML parsing.
See CODE HelloWorld.c
See CODE XmlBlasterAccessUnparsed.h
See http://sources.redhat.com/pthreads-win32/
See http://xmlrpc-c.sourceforge.net/
See http://www.gzip.org/zlib/
See REQ protocol.socket
See REQ client.c.queue
See REQ client.c.windowsCE
See REQ client.cpp.socket
See REQ client.cpp
See REQ interface
See REQ interface.connect
See TEST TestMethods

This page is generated from the requirement XML file xmlBlaster/doc/requirements/client.c.socket.xml

Back to overview