libmqmsgque 9.0
Loading...
Searching...
No Matches
libmqmsgque Tutorial

short introduction into libmqmsgque programming

INTRODUCTION

C-API: MQ_C_API - namespace for the LibMqMsgque "C"-API

The MqMsgque project is an infrastructure that allows multiple package-items to be linked together to act as one application.

PHILOSOPHY

The MqMsgque project is an infrastructure that allows multiple package-items to be linked together to act as one application.
To link, you need to distribute the work from one package-item to another package-item and wait for an answer or not.

Philosophy
Write Once → Run Everywhere

The package-item can be a thread, a separate local process that is started by fork or spawn, or even a network of multiple services on multiple hosts.

The package-item can be written in any language that is supported by the Programming-Language-Micro-Kernel support.
It even supports running multiple programming languages in a single piece of software.
Supported Languages are: (C,C++,C#,VB.NET,Java,Python,Ruby,Perl,PHP,Tcl or GO)

Strategy
It takes 4 years to write a programming-language, but it only takes 4 weeks to insert a micro-kernel.

The package-item is connected to one another via a pipe or a socket and is based on packages which are sent from one package-item to another package-item and back.

Conclusion
The MqMsgque project is used to manage a network of multiple package-items using an API that is available in all major programming-languages.

PROGRAMMING

The MqMsgque library is responsible for:

  • establishing and managing the package-items, local or remote
  • establishing and managing the connections between package-items
  • establishing and managing the routing between package-items
  • sending and receiving the package data, calling up a service
  • reading and writing data from or into a data-package
  • setup and maintain event-handling and scheduling
  • interception, distribution and processing of error messages

The LibMsgque library is separted into three programming-layers:

  1. The foundation-layer, used by the MqMsgque library programmer
  2. The kernel-layer, used by the Programming-Language-Micro-Kernel programmer
  3. The implementation-layer, used by the target-language programmer
foundation-layer
The foundation-layer implement the libmqmsgque library and is also responsible for the quality-target of the entire project.
  • establishing and managing the package-items
  • establishing and managing the connections
  • memory-management and garbage-collection
  • error-handling
  • logging and debugging
  • written in plain C
kernel-layer
The kernel-layer implement the Programming-Language-Micro-Kernel and is also responsible to generate and maintain the target-language-API source-code.
  • implementation of the managed-object technology
  • implementation of the token-stream-compiler technology
  • written in plain C, TCL and the target-language-API
implementation-layer
The implementation-layer is the API used by the target-language-programmer.
  • written in plain target-programming-language (C,C++,C#,VB.NET,Java,Python,Ruby,Perl,PHP,Tcl or GO)
Target
!! This documentation describe the implementation-layer and target the C programmer. !!

SERVICE CALL

C-API: MqContextC_Service_C_API - create and manage a service …

To provide a service is the main purpose of a server and the main-purpose of a client/server connection is to call a service and to process the result.
A service can be defined on the server or on the client. On the server a service can be initial setup with IServerSetup method and finally cleanup with IServerCleanup.

‍A service is created with the ServiceCreate and deleted with the ServiceDelete.

A service can be created and deleted during the entire life-cycle of the server or the client. If the server/client-context is deleted all services of the are deleted also.

‍A ServiceDelete is not required.

Creating or deleting a service is like granting or revoking the right to access a single feature.

eventloop

To receive a data-package on a service the event-loop have to be active. The event-loop ia always active on an synchronous-service-call . On a server the event-loop is started with ProcessEvent at startup.
synchronous-service-call
A synchronous-service-call always block and enter the event-loop to wait for an answer
asynchronous-service-call
A asynchronous-service-call always return immediately and the possible result is received on a callback

callback

A callback is the function called by a service and required to receive data from remote.
A callback is created with ServiceCreate or is part of the service-call.
A callback can be a service-identifer or a service-callback.
service-call with callback
SendEND_AND_CALLBACK, SendEND_AND_SUB, SendEND_AND_TRANSACTION
service-call without callback
SendEND, SendEND_AND_WAIT

Example from MyServer.c define the service SRV1 on the server-link-setup

#include "common.h"

// service to serve all incoming requests for token "HLWO"
static enum MkErrorE MyFirstService ( MQ_CALLBACK_SERVICE_CALL_ARGS ) {
  MqSendSTART_E (mqctx);
  MqSendV_E (mqctx, "%s World", MqReadC_e(mqctx));
error:
  return MqSendRETURN(mqctx);
}

// define a service as link between the token "HLWO" and the callback "MyFirstService"
static enum MkErrorE ServerSetup ( MQ_CALLBACK_SERVICE_CALL_ARGS ) {
  return MqServiceCreate(mqctx,"HLWO", MyFirstService, NULL, NULL, NULL);
}

// package-item
enum MkErrorE
MyServerFactory ( MQ_CALLBACK_FACTORY_CTOR_ARGS )
{ 
  MQ_CTX const mqctx = *contextP = MqContextCreate(NULL,tmpl);
  MqConfigSetServerSetup (mqctx, ServerSetup, NULL, NULL, NULL);
  return MK_OK;
}

// package-main
int main (int argc, MK_STRN argv[]) 
{
  MqRtSetup_NULL;

  // setup commandline arguments for later use

  MK_BFL largv = MkBufferListCreateVC(argc, argv);
  MQ_CTX mqctx = NULL;

  // create "MyServer" factory… and make it to the default.
  MqFactoryDefault(
    MqFactoryAdd(MK_ERROR_PANIC, MyServerFactory, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "MyServer")
  );
  // inspect commandline-argument for the "factory" to choose… and create a object
  MqFactoryNew_E (MqFactoryGetCalledL(largv), NULL, &mqctx);

  // start listen for incoming call's
  MqLinkCreate_E (mqctx, largv);
  MqCheckForLeftOverArguments_E (mqctx, largv);
  MqProcessEvent_E (mqctx,MQ_WAIT_FOREVER,MK_TIMEOUT_DEFAULT);
error:
  MkBufferListDelete(largv);
  MqExit_1(mqctx);
}

asynchronous-service-call

A asynchronous-service-call always return immediately and the possible result is received on a callback.

SendEND finish the send-data-block and call synchronous/asynchronous a remote-service
SendEND_AND_CALLBACK finish the send-data-block, call the remote service, do not-wait for the result but expect the result on a callback
SendEND_AND_TRANSACTION finish the send-data-block, call the remote service to do a longterm-transaction-call

synchronous-service-call

A synchronous-service-call always block and enter the event-loop to wait for an answer.

SendEND_AND_WAIT finish the send-data-block, call the remote service and wait for result…
SendEND_AND_SUB finish the send-data-block, call the remote service, do wait for the result and expect multiple sub-results on a callback

SENDING data

C-API: MqContextC_SendApi_C_API - construct an outgoing send-data-package

A data-package is send in two different scenarios:

  1. on a client to call a service on the server
  2. on a server to answer a service call from the client

Sending data is an active task and the opposite of reading data, which is a passive task. Active means that the send process is triggered by the software workflow or by the user.
For each basic type defined in MkBufferS, there is a send function and some help functions.

Send-Safe
Sending data is an atomic task and must not be interrupted between SendSTART and SendEND. The MqContextC HIGH API is uninterruptible by design.
Basic rule: process first and send all data last.

If timeout != 0 is used, the application enters the event loop and waits in the current process or thread for timeout seconds until the service call is finished.
While waiting for a result, the application can continue to work on other events that are in the same or in a different process or thread.

Recursion-Safe
If another-service-call arrives while waiting for a response from a previous-service-call, the previous-service-call will NOT end until processing of the other-service-call is complete.

Example-1: a service call, send and read a data-package

On a client: perform a service call

send the service-call SendSTARTSendTT... → SendEND_AND_WAIT
read the result packageReadTT... → ...

on a server: answer a service call

read the service-call ReadTT... → ...
send the result packageSendSTARTSendTT... → SendRETURN

Important in the code from above is the last command SendEND_AND_WAIT because this is just one of five possibilities:

command synchron database result
SendEND no no no
SendEND_AND_WAIT yes no single return data
SendEND_AND_SUB yes no multiple return data
SendEND_AND_CALLBACK no no single return data
SendEND_AND_TRANSACTION no yes two return data

To send a data-package is one task, to send it to the right receiver is an other one. The right receiver is identified using the token parameter argument. This parameter have to be a 4 character string. You'll probably ask "why 4?" the answer is that this string should be "human" readable and easy to "compare". As solution this string is mapped to a 4 byte integer used to find the proper key/value entry in the service-hash-table on the server. (in short: to search an integer is much faster as to search a string)

Example-2: (in C) At the client, calling the service and wait for an answer

...
// ... do some work
MqSendSTART_E(ctx); // init the Send-Buffer object
MqSendI_E(ctx,int_value); // 1. argument: a MK_INT value
MqSendC_E(ctx,"num:01"); // 2. argument: a MK_STRN value
MqSendB_E(ctx,mypicture,size); // 3. argument: a MK_BIN picture of size length
MqSendEND_AND_WAIT_E(ctx,"SRV1",60); // call service "SRV1" and wait max 60sec for the results
// ... get the results
MK_INT retI = MqReadI_e(ctx); // expect ONE integer as result
// ... do some work
return MK_OK;
error: // default error-handler
return MkErrorStack_1X(ctx); // on error, build up the error-stack
}
#define MkErrorStack_1X(...)
MkErrorE
MK_OK
signed int MK_INT
#define MqFactoryCT
instance-type as specific-instance-type for MqFactoryC in the target-programming-language (C,...
Definition msgque_mq.h:3071
#define MqSendC_E(...)
#define MqReadI_e(...)
#define MqSendSTART_E(...)
#define MqSendI_E(...)
#define MqSendEND_AND_WAIT_E(...)
#define MqSendB_E(...)
PUBLIC data structure for the libmqmsgque-specific-data
Definition msgque_mq.h:3804

... or using the MqContextC HIGH API

...
// ... do some work
MqSend_E(ctx, "W", "SRV1:ICB", int_value, "num:01", mypicture, size);
// ... get the results
MK_INT retI = MqReadI_e(ctx); // expect ONE integer as result
// ... do some work
return MK_OK;
error: // default error-handler
return MkErrorStack_1X(ctx); // on error, build up the error-stack
}
#define MqSend_E(...)

Example-3: (in C) At the server, answer the service call

...
static MkErrorE SRV1(MQ_CTX const ctx, MK_PTR data) { // the name "SRV1" can be free chosen
// ... do some work
MK_INT myInt = MqReadI_e(ctx); // read a MK_INT value
MK_STRN myStr = MqReadC_e(ctx); // read a MK_STR value
MK_BUF myPic = MqReadU_e(ctx); // read a MkBuffer64S object to store the picture data
// ... do some work
MqSendSTART_E(ctx); // init the Send-Buffer object
MqSendI_E(ctx,int_ret); // return argument: a MK_INT value
error: // something is wrong, error back
return MqSendRETURN(ctx); // send the package as an answer of a previous service-call
}
MK_PTRB * MK_PTR
const MK_STRB * MK_STRN
#define MqReadC_e(...)
#define MqReadU_e(...)
#define MqSendRETURN(...)

... or using the MqContextC HIGH API

...
static MkErrorE SRV1(MQ_CTX const ctx, MK_PTR data) { // the name "SRV1" can be free chosen
// ... do some work
MK_INT myInt = MqReadI_e(ctx); // read a MK_INT value
MK_STRN myStr = MqReadC_e(ctx); // read a MK_STR value
MK_BUF myPic = MqReadU_e(ctx); // read a MkBuffer64S object to store the picture data
// ... do some work
return MqSend(ctx, "R", "I", int_ret); // send the package as an answer of a previous service-call
}
#define MqSend(...)

READING data

C-API: MqContextC_ReadApi_C_API - extract data from an incoming read-data-package. …

A data-package is read in two different scenarios:

  • on a server to serve an incoming service-call from the client
  • on a client to process the return-data from a previous service-call

Reading data is a passive-task and the opposite of sending data, which is an active-task. Passive means that the reading process is triggered by an incoming-data-package and not by the software workflow or by the user.
There is a read function and some help functions for each basic type defined in MkBufferS .

Read-Safe
Each service-call has a private read-data-package. This means that during a service-call that is in progress, another service-call can be served without damaging the read-data-package of the current service-call.
Type-Safe
A data-item in a read-data-package is type safe, this mean that every read of a data-item have to match the data-type of the previous write. One exception is available, the cast from and to the string data-type (TYPE=C) is allowed.

Example from server.c read-safety: Make a nested service-call

static enum MkErrorE
Ot_CSV1 ( MQ_CALLBACK_SERVICE_CALL_ARGS )
{
  // read the input-data from the CSV1-service-call
  // client → server
  MK_INT retI, inI = MqReadI_e(mqctx) + 1;

  // call the CSV2-service at the client, wait 10sec for timeout
  // server → client → server
  MqSend_E(mqctx, "Wt", 10, "CSV2:I@I", inI, &retI);

  // answer the CSV1-service-call with the result from the CSV2-service-call
  // server → client
error:
  return MqSend(mqctx, "R", "I", retI+1);
}

STORAGE

internal storage

C-API: MqContextC_StorageApi_C_API - setup and manage a storage used to persist data-packages

The storage is divided into: INTERNAL and EXTERNAL storage. Only the read-data-package can be stored or dumped into the storage.

The read-data-package is saved into the storage using:

ServiceStorage setup a service listen on a MqContextC_ServiceApi_Identifer and save all read-data-package into the STORAGE
StorageExport export the read-data-package into the STORAGE

The read-data-package is restored from the storage using:

StorageImport import the storage-package into the read-data-package
ProxyForward send the entire read-data-package-data to the link-target
All this usually happen in an Event Handler

Some important facts of the storage-feature:

  • The internal-storage depends on SQLite.
  • The package-storage can be used to save all kind of package-data.
  • The longterm-transaction-package-data is allways saved into storage.
  • By default only an in-memory storage is in use, but this can be changed with StorageOpen or Storage
  • An entire service-call can be saved with ServiceStorage

The following internal storages are supported:

default
The default-storage is set with the configuration parameter --storage fileName and defaults to "#memdb#". If a package have to be saved into the storage and the storage is not open the default-storage id used. The open will always be performed. If an explicit storage is required the default can be changed or a storage can explicit be opened with StorageOpen. Keep in mind that the default-storage is a per-context configuration but only one storage per process or thread is currently supported.
memdb
This is the default storage and can be set explicitly with StorageOpen using the "#memdb#" parameter.
tmpdb
This storage is like an in-memory-storage but export data to the TEMPORARY filesystem if the application run out of memory. This storage can explicit be set with StorageOpen with the parameter "#tmpdb#".
filedb
This storage always work on files. Only this storage is persistent and can explicit be set with StorageOpen with the storageFile parameter.

Performance analyse:

  • The performance is tested with: Nhi1Exec perfclient.c --all --storage VALUE @ perfserver.c.
  • The parameter VALUE is set to #memdb#, #tmpdb# or a filedb file.
  • If –storage is not set the filedb database is used together with an internal file-name.
database performance host crash application crash info
memdb 30.000 data lost data lost non persistent
tempdb < 30.000 data lost data lost uses memory and/or temporary file
filedb (mem) 10.000 data lost data safe in memory filesystem
filedb (disc) 50 data safe data safe disc-speed is the key factor

external storage

C-API: MqDumpC_C_API - The MqDumpC object known as dmp or dump is used to export a libmqmsgque data package as binary

The dump is used to exchange data with external counterparts such as a database or a user-specific infrastructure.

There are 3 different function that deal with a dump:

  1. The DumpExport export the read-data-package from the calling context to a dump.
  2. The DumpImport imports the dump into the read-data-package of the calling context. All MqContextC READ API functions can be used to read the data from the read-data-package.
  3. The ProxyForward imports the dump into the send-data-package of the calling context. All MqContextC SEND API functions can be used to write the data into the send-data-package
See also
MqContextC PROXY API

LONG TERM TRANSACTION

C-API: MqSendEND_AND_TRANSACTION_RT - finish the send-data-block, call the remote service to do a longterm-transaction-call

A longterm-transaction is a service-call with guaranteed delivery. guaranteed mean that the transaction is using the MqContextC STORAGE API to keep every step persistent being able to recover a step if something unexpected happen.

To link the result with the calling-environment a private transaction-item is used to save the calling-environment to the local-storage. If the local-storage is persistent, the calling-environment will even survive an application restart.
If the result from the public service-call arrives, the transaction-item will be extracted from the result and the private calling-environment will be initialized from the local-storage.

To create a persistent-transaction the MqContextC STORAGE API have to be setup as persistent. By default, an in-memory MqContextC STORAGE API is used.

The longterm-transaction-call has TWO results…

  • the FIRST result is a acknowledge that the longterm-transaction was stored in the remote database
  • the SECOND result is the result of the service-call

In difference to SendEND_AND_WAIT and SendEND_AND_CALLBACK a longterm-transaction-call have to survive an application restart. To achieve this goal… two features have to be available to process the results:

  • a transaction-callback as a service created with ServiceCreate
  • a transaction-item as a environment created with SendT_STARTSendT_END and stored into an internal database.

transaction-item

The transaction-item is the entry-id from the local internal database and is the public handle of the private data. If the transaction-item-private-data should be persistent (survive an application restart) the internal database have to be persistent using the Storage option.
The transaction-item-private-data requires a SendT_STARTSendT_END at the beginning of the send-data-package.
The list of data-items in the transaction-item-private-data have to be provided by the programmer and is used to initialise the environment in the callback (for example an external database-entry-id).
In the transaction-callback the transaction-item-private-data have to be extracted with ReadT_STARTReadT_END at the beginning of the read-data-package.

transaction-callback

The transaction-callback have to be a MqContextC_ServiceApi_Identifer defined with ServiceCreate in the application setup code (like IServerSetup) and have to be available after an application restart.

If an error is raised on the server during the database-insert the function will return this error immediately. During waiting for the return the event-loop is used to process other events. Use IEvent to add your tasks into the event loop.

Parameters
[in]mkrtthe MkRuntimeS instance to work on - the runtime argument, used by MK_RT_CALL (C-only)
[in]ctxthe MqContextS instance to work on
[in]tokenthe MqContextC SERVICE API to identify the service
[in]callbackthe MqContextC_ServiceApi_Identifer of the MqContextC_ServiceApi_Callback
[in]timeoutin seconds until a timeout-error is raised (possible values like ProcessEvent) (MK_TIMEOUT_DEFAULT=MK_TIMEOUT_USER)
Returns
set the MkErrorC to the status MK_OK, MK_CONTINUE or MK_ERROR

Example from MyTransaction.c make a logterm-transaction-call using the LOW and the HIGH api

#include "msgque_mq.h"

static enum MkErrorE callback( MQ_CALLBACK_SERVICE_CALL_ARGS ) {
  MqReadT_START_E(mqctx);
  MK_STRN myPrivateHandle = MqReadC_e(mqctx);
  MqReadT_END(mqctx);
  MK_INT myServiceResult = MqReadI_e(mqctx);

  fprintf(stdout,"myPrivateHandle=%s, myServiceResult=%d\n", myPrivateHandle, myServiceResult);
  fflush(stdout);

error:
  return MkErrorStack_E(mqctx);
}

int main (int argc, MK_STRN argv[]) 
{
  MkRtSetup_NULL;

  MQ_CTX ctx = MqContextCreate(NULL,NULL);

  MqConfigSetName(ctx, "MyTransaction");

  // setup commandline arguments used for parsing
  MK_BFL args = MkBufferListCreateVC(argc, argv);

  // check if the '--token' option is available, default "SRVC"
  MK_STRN token = MkBufferListCheckOptionC_e(args, "--token", "SRVC", true);

  // connect to the server
  MqLinkCreate_E(ctx, args);

  // register callback
  MqServiceCreate_E(ctx, "CLB1", callback, NULL, NULL, NULL);
   
  // send block using the LOW-Api
  MqSendSTART_E(ctx);
  MqSendT_START_E(ctx);
  MqSendC_E(ctx, "Privat_Data_1");
  MqSendT_END_E(ctx);
  MqSendI_E(ctx, 11111);
  MqSendEND_AND_TRANSACTION_E(ctx, token, "CLB1", MK_TIMEOUT_DEFAULT);
   
  // send block using the HIGN-Api -> same as above, but shorter
  char buffer[30];
  sprintf(buffer, "%s:(C)I",token);
  MqSend_E(ctx, "T", "CLB1", buffer, "Privat_Data_2", 22222);
   
  // now we wait for exact ONE result of the "ctx"
  MqProcessEvent_E(ctx, MQ_WAIT_OWN, MK_TIMEOUT_DEFAULT);

error:
  MkBufferListDelete(args);
  // delete the context using the libmqmsgque APPLICATION-DTOR function "MqExit_1"
  MqExit_1(ctx);
}

SERVER

A libmqmsgque server requires the following setup:

  1. file: example/c/MyServer.c
  2. an instance of the abstract class ContextCreate
  3. the interface MqFactoryC to create a new application instance
  4. the interface IServerSetup and or IServerCleanup

Example from MyServer.c The minimal server looks like:

#include "common.h"

// service to serve all incoming requests for token "HLWO"
static enum MkErrorE MyFirstService ( MQ_CALLBACK_SERVICE_CALL_ARGS ) {
  MqSendSTART_E (mqctx);
  MqSendV_E (mqctx, "%s World", MqReadC_e(mqctx));
error:
  return MqSendRETURN(mqctx);
}

// define a service as link between the token "HLWO" and the callback "MyFirstService"
static enum MkErrorE ServerSetup ( MQ_CALLBACK_SERVICE_CALL_ARGS ) {
  return MqServiceCreate(mqctx,"HLWO", MyFirstService, NULL, NULL, NULL);
}

// package-item
enum MkErrorE
MyServerFactory ( MQ_CALLBACK_FACTORY_CTOR_ARGS )
{ 
  MQ_CTX const mqctx = *contextP = MqContextCreate(NULL,tmpl);
  MqConfigSetServerSetup (mqctx, ServerSetup, NULL, NULL, NULL);
  return MK_OK;
}

// package-main
int main (int argc, MK_STRN argv[]) 
{
  MqRtSetup_NULL;

  // setup commandline arguments for later use

  MK_BFL largv = MkBufferListCreateVC(argc, argv);
  MQ_CTX mqctx = NULL;

  // create "MyServer" factory… and make it to the default.
  MqFactoryDefault(
    MqFactoryAdd(MK_ERROR_PANIC, MyServerFactory, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "MyServer")
  );
  // inspect commandline-argument for the "factory" to choose… and create a object
  MqFactoryNew_E (MqFactoryGetCalledL(largv), NULL, &mqctx);

  // start listen for incoming call's
  MqLinkCreate_E (mqctx, largv);
  MqCheckForLeftOverArguments_E (mqctx, largv);
  MqProcessEvent_E (mqctx,MQ_WAIT_FOREVER,MK_TIMEOUT_DEFAULT);
error:
  MkBufferListDelete(largv);
  MqExit_1(mqctx);
}

The server is started as network visible TCP server listen on PORT 2345 using a THREAD for every new connection request:

  • MyServer –tcp –port 2345 –thread

If you are using UNIX and if you want to setup a high-performance local server then use the build-in UDS (Unix-Domain-Sockets) capability to listen on the FILE /path/to/any/file.uds instead on a network port:

  • MyServer –uds –file /path/to/any/file.uds –thread

Three things are important:

  1. the send style of functions
  2. the enum MkErrorE MqServiceCreate(MQ_CTX ctx, MQ_TOK token, MqServiceCallbackF fCall, MK_CBP callback, MqDataFreeF fFree, MkMarkF fMark) function
  3. a connected context of type (constructor) MQ_CTX MqContextCreate(MK_TYP type, MQ_CTX tmpl)

Sending data is done using a minimum of 2 steps:

  1. First: start a data package with enum MkErrorE MqSendSTART(MQ_CTX ctx)
  2. Last: submit the a data package to the link target using one of:

The first three enum MkErrorE MqSendEND(MQ_CTX ctx, MQ_TOK token, MK_TIME_T timeout)... functions are used to call a remote service and the last one is used to answer an incoming service call. In-between enum MkErrorE MqSendSTART(MQ_CTX ctx) and enum MkErrorE MqSendEND(MQ_CTX ctx, MQ_TOK token, MK_TIME_T timeout) ... other MqContextC SEND API style commands are available to fill the data package with data.

Services are created with the enum MkErrorE MqServiceCreate(MQ_CTX ctx, MQ_TOK token, MqServiceCallbackF fCall, MK_CBP callback, MqDataFreeF fFree, MkMarkF fMark) function. The first parameter is a 4 byte Token as public name. 4 byte is required because this string is mapped to a 4 byte integer for speed reason. The second parameter is an object providing the MqContextC_ServiceApi_Callback interface.

The MqContextC_ServiceApi_Callback is called for every incoming service-request which belongs to token.

CLIENT

A libmqmsgque client requires the following setup:

Example from MyClient.c The minimal client looks like:

#include "debug_mq.h"
#include "msgque_mq.h"

int main (int argc, MK_STRN argv[]) 
{
  MkRtSetup_NULL;
  struct MkBufferListS * largv = MkBufferListCreateVC(argc, argv);
  // create a context using the static libmqmsgque CTOR function "MqContextCreate"
  MQ_CTX ctx = MqContextCreate(NULL,NULL);
  MqConfigSetName(ctx, "MyClient");
  MqLinkCreate_E (ctx, largv);
  MqCheckForLeftOverArguments_E (ctx, largv);
  MqSend_E (ctx, "W", "HLWO:C", "Hello");
  fprintf(stdout,"%s\n", MqReadC_e (ctx));
  fflush(stdout);

error:
  MkBufferListDelete(largv);
  // delete the context using the libmqmsgque APPLICATION-DTOR function "MqExit_1"
  MqExit_1(ctx);
}

Example: To call a network visible TCP server listen on PORT 2345 use:

  • MyClient –tcp –port 2345
  • Hello World

Example: To call a network visible UDP server listen on FILE /path/to/any/file.uds use:

  • MyClient –uds –file /path/to/any/file.uds
  • Hello World

Example: To call a local server started by the client using PIPE communication use:

  • MyClient @ MyServer
  • Hello World

FILTER

The FILTER MODE is used to define a command pipeline.

Example from manfilter.c A minimal filter looks like:

#include "common.h"

static enum MkErrorE  FTR( MQ_CALLBACK_SERVICE_CALL_ARGS ) {
  MK_STRN str;
  MQ_CTX ftr;
  MqSlaveGetFilter_E (mqctx, &ftr);
  MqSendSTART_E (ftr);
  while (MqReadItemExists(mqctx)) {
    MqReadC_E (mqctx, &str);
    MkBufferSetV(MkBUF(&mqctx->ctxbuf),"<%s>", str);
    MqSendU_E (ftr, MkBUF(&mqctx->ctxbuf));
  }
  MqSendEND_AND_WAIT_E (ftr, "+FTR", MK_TIMEOUT_USER);
error:
  return MqSendRETURN (mqctx);
}
int main (int argc, MK_STRN argv[]) 
{
  MkRtSetup_NULL;
  struct MkBufferListS * largv = MkBufferListCreateVC(argc, argv);
  MQ_CTX mqctx = MqContextCreate(NULL,NULL);
  MqConfigSetName (mqctx, "ManFilter");
  MqConfigSetIsServer (mqctx, true);
  MqLinkCreate_E (mqctx, largv);
  MqServiceCreate_E (mqctx, "+FTR", FTR, NULL, NULL, NULL);
  MqServiceProxy_E (mqctx, "+EOF", MQ_SLAVE_FILTER);
  MqCheckForLeftOverArguments_E (mqctx, largv);
  MqProcessEvent_E (mqctx,MQ_WAIT_FOREVER,MK_TIMEOUT_DEFAULT);
error:
  MkBufferListDelete(largv);
  MqExit_1(mqctx);
}

Use manfilter in a LibMqMsgque command pipeline:

  • echo -e "1:2:3\na:b:c" | atool split -d : @ manfilter @ atool join -d :