CBCP Tutorials

Welcome to the tutorials page. The tutorials here provide example code for you to examine as well as descriptions for each part of it. Note that the examples are only compatible with Linux.

Each tutorial has a downloadable zip archive, which contains the full source and all other required files for each example. Each zip archive is self-contained and you can run their build scripts and run the executables with the database files out of the box.

If you want to modify the network configurations of the examples or write your own, then you can download the source code for the configuration program here.


CBCP Example 1: Hello, World!

This tutorial guides you through the basic operation of CBCP. The example in this tutorial shows you how to send a command from a client to a server. The example command makes the server print "Hello, World!". The following tutorials will expand on this command.

The below network configuration file shows the hosts, commands, and capabilities involved in the example. There are only two hosts here and a single command that the client can send to the server.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
!CBCP 1.0

!HOSTS
Client ; TCP,127.0.0.1:9000
Server ; TCP,127.0.0.1:9002

!INTERFACES
Hello World ; Hello

!IMPLEMENTS
Server ; Hello World

!CAPABILITIES
Client ; Server ; Hello World ; Hello

The client and the server share some common elements, but both will be explained in detail. They will be presented piecewise, with explanations for each section of the code.

Client

The overall operation of the client is to initialize CBCP and send the command to the server, after which it terminates.

Use of cbcp requires including the cbcp library. This example uses TCP as its transport layer and includes a matching library. The TCP library is a single-header library and the macro definition includes the implementation.

1
2
3
4
5
#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>

Initialization has two components. The first component is initialization of the transport layer so that CBCP can utilize it. The second component is initialization of CBCP itself, which requires a filepath to a cbcpdb file. The cbcpdb file contains encryption keys and other necessary data. The example archive contains cbcpdb files for you to test the example with.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/*
** Initialize CBCP by loading the database file for this host
*/

const char *cbcp_database_filepath = argv[1];
CBCP_State *cbcp;

cbcp_net_tcp_init(0);
cbcp = cbcp_init(cbcp_database_filepath);

if (cbcp == NULL) {
	fprintf(stderr, "Could not initialize CBCP.\n");
	return 1;
}

The client has to initialize the command before it can be used. A pointer to the command handle is returned, which can be used to send commands to the server. The command contains all the information required to send commands, including the destination, which is "Server" in this case. The returned command is always NULL if an error occurs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/*
** Init commands
*/

CBCP_Command *command_hello_world;
command_hello_world = cbcp_client_init_command(
	cbcp,
	"Server",
	"Hello World",
	"Hello");

if (!command_hello_world) {
	fprintf(stderr, "Could not initialize required command.\n");
	return 1;
}

All initialization is complete and the client is ready to send a command to the server. The command is sent as shown using the command pointer that was initialized earlier. The other parameters are not important for this example and should be ignored. The return value can be used to determine success or failure of sending the command.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/*
** Client code here
*/

printf("Sending cbcp command to the server.\n");

CBCP_Response response;

response = cbcp_client_send_command(
	command_hello_world,
	NULL,
	0,
	NULL,
	0);

if(response.success)
	printf("Server has received the command.\n");
else
	printf("Sending command to server was not successful!\n");

Server

The server, just like the client, leads with initializing CBCP. However, after the initialization it can simply defer execution to the library.

The includes for the server are the same as they are for the client.

1
2
3
4
5
#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>

First, let us look at the function we want to execute when the client sends the command. The command is very simple in this case, as it only prints "Hello, World!". The argument can be ignored for now.

1
2
3
4
static void command_hello_world(CBCP_Command_Args *args) {
	(void)args;
	printf("Hello, World!\n");
}

Moving onto the main program, we first need to look at the initialization of CBCP, which is identical to that of the client.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/*
** Initialize CBCP by loading the database file for this host
*/

const char *cbcp_database_filepath = argv[1];
CBCP_State *cbcp;

cbcp_net_tcp_init(0);
cbcp = cbcp_init(cbcp_database_filepath);

if (cbcp == NULL) {
	fprintf(stderr, "Could not initialize CBCP.\n");
	return 1;
}

The server has to query CBCP similarly to how the client does, but the model is different. The server doesn't get a command handle back, but instead registers a function callback. When the server receives a command, then the library calls the callback. This method leaves the entire verification process to CBCP, which protects the usage code from mistakes that could lead to a non-verified command to somehow get executed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/*
** Bind command callbacks
*/

if (!cbcp_server_init_command(
	cbcp,
	"Hello World",
	"Hello",
	command_hello_world,
	NULL,
	0)
) {
	fprintf(stderr, "Could not initialize served command.\n");
	return 1;
}

Starting the cbcp server is as simple as calling into the library, which takes care of the server operation and calls the callback when the command from the client is received.. The program is assumed to run indefinitely and cbcp_server_start doesn't terminate. In order to run the server on a separate thread, use cbcp_server_start_async.

1
2
3
4
5
6
7
/*
** Start the cbcp server
*/

printf("Starting CBCP server for Hello World application.\n");

cbcp_server_start(cbcp);

Client Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/*
** Hello World Client
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Init commands
	*/

	CBCP_Command *command_hello_world;
	command_hello_world = cbcp_client_init_command(
		cbcp,
		"Server",
		"Hello World",
		"Hello");

	if (!command_hello_world) {
		fprintf(stderr, "Could not initialize required command.\n");
		return 1;
	}

	/*
	** Client code here
	*/

	printf("Sending cbcp command to the server.\n");

	CBCP_Response response;

	response = cbcp_client_send_command(
		command_hello_world,
		NULL,
		0,
		NULL,
		0);

	if(response.success)
		printf("Server has received the command.\n");
	else
		printf("Sending command to server was not successful!\n");

	return 0;
}

Server Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/*
** Hello World Server
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>



/*
** CBCP command predeclarations
*/
static void command_hello_world(CBCP_Command_Args *args);


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Bind command callbacks
	*/

	if (!cbcp_server_init_command(
		cbcp,
		"Hello World",
		"Hello",
		command_hello_world,
		NULL,
		0)
	) {
		fprintf(stderr, "Could not initialize served command.\n");
		return 1;
	}

	/*
	** Start the cbcp server
	*/

	printf("Starting CBCP server for Hello World application.\n");

	cbcp_server_start(cbcp);

	return 0;
}

static void command_hello_world(CBCP_Command_Args *args) {
	(void)args;
	printf("Hello, World!\n");
}
Jump back to top

CBCP Example 2: Hello, Name!

This tutorial shows you what changes are necessary from example 1 in order to send data from the client to the server. The network setup is identical and so is most of the code, so only the differences are explained.

Client

The only change is about sending data with the command, so all initialization is unchanged. The difference lies in that arguments are now passed to cbcp_client_send_command. This example code takes the name to send from the command line arguments. The name and the name length are simply passed as the first two arguments to cbcp_client_send_command, and the server will receive it along with the command.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
** Client code here
*/

const char *name = argv[2];
unsigned int name_length = strlen(name);

printf(
	"Sending cbcp command to the server with payload %.*s.\n",
	name_length,
	name);

CBCP_Response response;

response = cbcp_client_send_command(
	command_hello_world,
	(void*)name,
	name_length,
	NULL,
	0);

if(response.success)
	printf("Server has received the command.\n");
else
	printf("Sending command to server was not successful!\n");

Server

The server requires minimal change as well. Because we are only changing how the command is used, the only thing we need to change is the callback function. The data sent by the client is available through the args struct, as well as the length of the data. In this example we don't do any sanity checking on the received data, e.g. to see if it contains terminal control characters, which is something that should be done in real applications.

1
2
3
4
5
6
static void command_hello_world(CBCP_Command_Args *args) {
	char *message = (char *)args->payload;
	unsigned int message_length = args->payload_length;

	printf("Hello, %.*s!\n", message_length, message);
}

Client Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
** Hello Name Client
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>
#include <string.h>


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 3) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Init commands
	*/

	CBCP_Command *command_hello_world;
	command_hello_world = cbcp_client_init_command(
		cbcp,
		"Server",
		"Hello Name",
		"Hello");

	if (!command_hello_world) {
		fprintf(stderr, "Could not initialize required command.\n");
		return 1;
	}

	/*
	** Client code here
	*/

	const char *name = argv[2];
	unsigned int name_length = strlen(name);

	printf(
		"Sending cbcp command to the server with payload %.*s.\n",
		name_length,
		name);

	CBCP_Response response;

	response = cbcp_client_send_command(
		command_hello_world,
		(void*)name,
		name_length,
		NULL,
		0);

	if(response.success)
		printf("Server has received the command.\n");
	else
		printf("Sending command to server was not successful!\n");

	return 0;
}

Server Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*
** Hello Name Server
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>



/*
** CBCP command predeclarations
*/
static void command_hello_world(CBCP_Command_Args *args);


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Bind command callbacks
	*/

	if (!cbcp_server_init_command(
		cbcp,
		"Hello Name",
		"Hello",
		command_hello_world,
		NULL,
		0)
	) {
		fprintf(stderr, "Could not initialize served command.\n");
		return 1;
	}

	/*
	** Start the cbcp server
	*/

	printf("Starting CBCP server for Hello Name application.\n");

	cbcp_server_start(cbcp);

	return 0;
}

static void command_hello_world(CBCP_Command_Args *args) {
	char *message = (char *)args->payload;
	unsigned int message_length = args->payload_length;

	printf("Hello, %.*s!\n", message_length, message);
}
Jump back to top

CBCP Example 3: Hello, Name! with Response

This tutorial expands upon example 2 by adding data to the response from the server. Again, only the differences are explained.

Client

To receive the response data the client must provide a buffer that cbcp can store the data in. This buffer needs to be sufficiently large to receive the complete packet over the network. In this example the buffer has been made excessively large to avoid any potential problems with the buffer size.

To receive the response in the buffer, the client simply passes it through the final two arguments of cbcp_client_send_command. When the call returns, the buffer has been filled. However, the location in the buffer where the response data is stored is not known, and the response struct therefore provides the location in the payload field.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
** Client code here
*/

const char *name = argv[2];
unsigned int name_length = strlen(name);

printf("Sending cbcp command to the server with payload %.*s.\n",
	name_length,
	name);

CBCP_Response response;
char response_buffer[4096];

response = cbcp_client_send_command(
	command_hello_world,
	(void*)name,
	name_length,
	response_buffer,
	sizeof(response_buffer));

if(response.success)
{
	printf("Server has received the command.\n");
	if(response.payload != NULL)
	{
		printf("Length of response: %d\n", response.payload_length);
		printf(
			"Response: %.*s\n",
			response.payload_length,
			(char*)response.payload);
	}
	else
	{
		printf("No payload?\n");
	}
}
else
	printf("Sending command to server was not successful!\n");

Server

In order for the server to send data with the response we need to adjust the binding parameters. The final parameter of the binding is the maximal size of the data we may send in response to a command. In this case we set it to 64 because we do not expect to write a longer result string in response to the name the client sends us.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
if (!cbcp_server_init_command(
	cbcp,
	"Hello Name",
	"Hello",
	command_hello_world,
	NULL,
	64)
) {
	fprintf(stderr, "Could not initialize served command.\n");
	return 1;
}

Now that we have made sure to bind the command so that we have buffer space to work with, we modify the callback to write send response data. The args struct contains a pointer to the location that the response can be written to as well as the writable size. Sending the response is simple: just write to the buffer starting at the specified address and set the buffer length in the args struct through the response_payload_length field.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
static void command_hello_world(CBCP_Command_Args *args) {
	char *message = (char *)args->payload;
	unsigned int message_length = args->payload_length;

	char *response_payload = (char *)args->response_payload;
	int max_payload_length = args->response_payload_max_length;
	int string_size = snprintf(
		response_payload,
		max_payload_length,
		"Hello, %.*s!",
		message_length,
		message);
	if(string_size < max_payload_length)
		args->response_payload_length = string_size;
	else
		args->response_payload_length = max_payload_length;
	printf(
		"A hello was received from %.*s! Returning the greeting!\n",
		message_length,
		message);
}

Client Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*
** Hello Name Client
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>
#include <string.h>


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 3) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath> <name>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Init commands
	*/

	CBCP_Command *command_hello_world;
	command_hello_world = cbcp_client_init_command(
		cbcp,
		"Server",
		"Hello Name",
		"Hello");

	if (!command_hello_world) {
		fprintf(stderr, "Could not initialize required command.\n");
		return 1;
	}

	/*
	** Client code here
	*/

	const char *name = argv[2];
	unsigned int name_length = strlen(name);

	printf("Sending cbcp command to the server with payload %.*s.\n",
		name_length,
		name);

	CBCP_Response response;
	char response_buffer[4096];

	response = cbcp_client_send_command(
		command_hello_world,
		(void*)name,
		name_length,
		response_buffer,
		sizeof(response_buffer));

	if(response.success)
	{
		printf("Server has received the command.\n");
		if(response.payload != NULL)
		{
			printf("Length of response: %d\n", response.payload_length);
			printf(
				"Response: %.*s\n",
				response.payload_length,
				(char*)response.payload);
		}
		else
		{
			printf("No payload?\n");
		}
	}
	else
		printf("Sending command to server was not successful!\n");

	return 0;
}

Server Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
** Hello Name Server
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>


/*
** CBCP command predeclarations
*/
static void command_hello_world(CBCP_Command_Args *args);


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Bind command callbacks
	*/

	if (!cbcp_server_init_command(
		cbcp,
		"Hello Name",
		"Hello",
		command_hello_world,
		NULL,
		64)
	) {
		fprintf(stderr, "Could not initialize served command.\n");
		return 1;
	}

	/*
	** Start the cbcp server
	*/

	printf("Starting CBCP server for Hello Name application.\n");

	cbcp_server_start(cbcp);

	return 0;
}

static void command_hello_world(CBCP_Command_Args *args) {
	char *message = (char *)args->payload;
	unsigned int message_length = args->payload_length;

	char *response_payload = (char *)args->response_payload;
	int max_payload_length = args->response_payload_max_length;
	int string_size = snprintf(
		response_payload,
		max_payload_length,
		"Hello, %.*s!",
		message_length,
		message);
	if(string_size < max_payload_length)
		args->response_payload_length = string_size;
	else
		args->response_payload_length = max_payload_length;
	printf(
		"A hello was received from %.*s! Returning the greeting!\n",
		message_length,
		message);
}
Jump back to top

CBCP Example 4: Addition

This tutorial focuses on the use of binary data in the exchanged messages. We are building towards an example where the server provides multiple commands and where we have more than one client. For now, we use a single client and a server that only offers a command that adds numbers on behalf of the client.

The new network definition is given below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
!CBCP 1.0

!HOSTS
Client ; TCP,127.0.0.1:9000
Server ; TCP,127.0.0.1:9002

!INTERFACES
Calculation ; Add

!IMPLEMENTS
Server ; Calculation

!CAPABILITIES
Client ; Server ; Calculation ; Add

Client

Sending binary data is in principle no more difficult than sending text. In this example the two numbers to be added are retrieved from the command line arguments and written into an array. Sending the two numbers only requires using the array as the payload buffer, the size of which can be taken with sizeof.

The response from the server is also a binary data, particularly a number. We can print it out by simply accessing it as such.

Note that we are not encoding the data. We are for the purposes of this example assuming that both machines have the same endianness

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
** Client code here
*/

printf("Sending cbcp command to the server with payload %d; %d.\n", a, b);

uint32_t a = atoi(argv[2]);
uint32_t b = atoi(argv[3]);

uint32_t payload[2] = {0};
payload[0] = a;
payload[1] = b;

CBCP_Response response;
char response_buffer[4096];

response = cbcp_client_send_command(
	command_add,
	(void*)payload,
	sizeof(payload),
	response_buffer,
	sizeof(response_buffer));

if(response.success)
{
	printf("Server has received the command.\n");
	if(response.payload != NULL)
	{
		printf("Length of response: %d\n", response.payload_length);
		printf("Response: %d\n", *((uint32_t*)response.payload));
	}
	else
	{
		printf("No payload?\n");
	}
}
else
	printf("Sending command to server was not successful!\n");

Server

The difference between text and binary data is handled similarly on the server side. We only need to modify the callback, where the greatest difference is that we do some sanity checking to ensure that the data conforms to a fixed format, and that we can send a sufficiently large payload in return.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static void command_add(CBCP_Command_Args *args) {
	uint32_t *message = (uint32_t *)args->payload;
	unsigned int message_length = args->payload_length;
	if(message_length != sizeof(uint32_t)*2)
	{
		fprintf(stderr, "message_length is not equal to two 'uint32's!\n"
		                "\tmessage_length: %d\n", message_length);
		return;
	}
	char *response_payload = (char *)args->response_payload;
	unsigned int max_payload_length = args->response_payload_max_length;
	if(max_payload_length < sizeof(uint32_t))
	{
		fprintf(stderr, "max_payload_length is too small!\n");
		return;
	}

	uint32_t a = message[0];
	uint32_t b = message[1];
	uint32_t result = a + b;

	printf(
		"Adding %d and %d. result: %d!\n",
		a, b, result);

	args->response_payload_length = sizeof(uint32_t);
	memcpy(response_payload, (void*)&result, sizeof(result));
}

Client Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
** Hello Name Client
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 4) {
		fprintf(
			stderr,
			"Usage:\n\t%s <cbcp_database_filepath> <a> <b>\n",
			argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Init commands
	*/

	CBCP_Command *command_add;
	command_add = cbcp_client_init_command(
		cbcp,
		"Server",
		"Calculation",
		"Add");

	if (!command_add) {
		fprintf(stderr, "Could not initialize required command.\n");
		return 1;
	}

	/*
	** Client code here
	*/

	printf("Sending cbcp command to the server with payload %d; %d.\n", a, b);

	uint32_t a = atoi(argv[2]);
	uint32_t b = atoi(argv[3]);

	uint32_t payload[2] = {0};
	payload[0] = a;
	payload[1] = b;

	CBCP_Response response;
	char response_buffer[4096];

	response = cbcp_client_send_command(
		command_add,
		(void*)payload,
		sizeof(payload),
		response_buffer,
		sizeof(response_buffer));

	if(response.success)
	{
		printf("Server has received the command.\n");
		if(response.payload != NULL)
		{
			printf("Length of response: %d\n", response.payload_length);
			printf("Response: %d\n", *((uint32_t*)response.payload));
		}
		else
		{
			printf("No payload?\n");
		}
	}
	else
		printf("Sending command to server was not successful!\n");

	return 0;
}

Server Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/*
** Hello Name Server
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>


/*
** CBCP command predeclarations
*/
static void command_add(CBCP_Command_Args *args);


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Bind command callbacks
	*/

	if (!cbcp_server_init_command(
		cbcp,
		"Calculation",
		"Add",
		command_add,
		NULL,
		sizeof(uint32_t))
	) {
		fprintf(stderr, "Could not initialize served command.\n");
		return 1;
	}

	/*
	** Start the cbcp server
	*/

	printf("Starting CBCP server for Hello Name application.\n");

	cbcp_server_start(cbcp);

	return 0;
}

static void command_add(CBCP_Command_Args *args) {
	uint32_t *message = (uint32_t *)args->payload;
	unsigned int message_length = args->payload_length;
	if(message_length != sizeof(uint32_t)*2)
	{
		fprintf(stderr, "message_length is not equal to two 'uint32's!\n"
		                "\tmessage_length: %d\n", message_length);
		return;
	}
	char *response_payload = (char *)args->response_payload;
	unsigned int max_payload_length = args->response_payload_max_length;
	if(max_payload_length < sizeof(uint32_t))
	{
		fprintf(stderr, "max_payload_length is too small!\n");
		return;
	}

	uint32_t a = message[0];
	uint32_t b = message[1];
	uint32_t result = a + b;

	printf(
		"Adding %d and %d. result: %d!\n",
		a, b, result);

	args->response_payload_length = sizeof(uint32_t);
	memcpy(response_payload, (void*)&result, sizeof(result));
}
Jump back to top

CBCP Example 5: Calculation

In this tutorial we expand on the number of commands that can be requested on the server. We build on top of example 4 by adding subtraction, multiplication and division.

The new updated definition is given below. There are two clients in this configuration, which have different capabilities. Note that we will have the clients share implementation, but will simply not be able to send the same commands.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
!CBCP 1.0

!HOSTS
Client ; TCP,127.0.0.1:9000
Server ; TCP,127.0.0.1:9002

!INTERFACES
Calculation ; Add

!IMPLEMENTS
Server ; Calculation

!CAPABILITIES
Client ; Server ; Calculation ; Add

Client

The increased selection of commands is not any more complicated for the client. We simply attempt to initialize all of them, and if a command is not available then the pointer will simply be NULL. For this reason, the command pointers will be NULL for the comands that each client cannot use.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/*
** Init commands
*/

CBCP_Command *command_add;
CBCP_Command *command_subtract;
CBCP_Command *command_multiply;
CBCP_Command *command_divide;

command_add = cbcp_client_init_command(
	cbcp, "Server", "Calculation", "Add");
command_subtract = cbcp_client_init_command(
	cbcp, "Server", "Calculation", "Subtract");
command_multiply = cbcp_client_init_command(
	cbcp, "Server", "Calculation", "Multiply");
command_divide = cbcp_client_init_command(
	cbcp, "Server", "Calculation", "Divide");

The client once again takes its numbers from the command line, but now also takes the command to execute from the command line. We use the fact that a command is NULL if it isn't available for accurate error reporting. We have for the sake of simplicity decided on four commands that share the same format so that the rest of the code is the same for all of them. The remainder of the code is the same as for example 4.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*
** Client code here
*/

uint32_t a = atoi(argv[2]);
char operator = *argv[3];
uint32_t b = atoi(argv[4]);

CBCP_Command *command_to_execute = NULL;

switch(operator)
{
	case '+':
		command_to_execute = command_add;
		break;
	case '-':
		command_to_execute = command_subtract;
		break;
	case '*':
		command_to_execute = command_multiply;
		break;
	case '/':
		command_to_execute = command_divide;
		break;
	default:
		fprintf(stderr, "Unknown operator: '%c'\n", operator);
		return 1;
}

if(command_to_execute == NULL)
{
	fprintf(stderr, "Missing capability to execute command '%c'\n", operator);
	return 1;
}

printf(
	"Sending cbcp command '%c' to the server with payload %d; %d.\n",
	operator, a, b);

uint32_t payload[2] = {0};
payload[0] = a;
payload[1] = b;


response = cbcp_client_send_command(
	command_to_execute,
	(void*)payload,
	sizeof(payload),
	response_buffer,
	sizeof(response_buffer));

if(response.success)
{
	printf("Server has received the command.\n");
	if(response.payload != NULL)
	{
		printf("Length of response: %d\n", response.payload_length);
		printf("Response: %d\n", *((uint32_t*)response.payload));
	}
	else
	{
		printf("No payload?\n");
	}
}
else
	printf("Sending command to server was not successful!\n");

Server

The increased number of commands is handled quite easily. The server simply binds more commands and then everything is set up.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
** Bind command callbacks
*/

if (!cbcp_server_init_command(
	cbcp,
	"Calculation", "Add", command_add,
	NULL, sizeof(uint32_t))
) {
	fprintf(stderr, "Could not initialize add command.\n");
	return 1;
}

if (!cbcp_server_init_command(
	cbcp,
	"Calculation", "Subtract", command_subtract,
	NULL, sizeof(uint32_t))
) {
	fprintf(stderr, "Could not initialize subtract command.\n");
	return 1;
}

if (!cbcp_server_init_command(
	cbcp,
	"Calculation", "Multiply", command_multiply,
	NULL, sizeof(uint32_t))
) {
	fprintf(stderr, "Could not initialize multiply command.\n");
	return 1;
}

if (!cbcp_server_init_command(
	cbcp,
	"Calculation", "Divide", command_divide,
	NULL, sizeof(uint32_t))
) {
	fprintf(stderr, "Could not initialize divide command.\n");
	return 1;
}

The callback for addition is the exact same as in example 4. The other callbacks are almost identical to the callback for addition.

Client Source Code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
** Hello Name Client
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 5) {
		fprintf(
			stderr,
			"Usage:\n\t%s <cbcp_database_filepath> <a> <operator> <b>\n",
			argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;
	CBCP_Response response;
	char response_buffer[4096];

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Init commands
	*/

	CBCP_Command *command_add;
	CBCP_Command *command_subtract;
	CBCP_Command *command_multiply;
	CBCP_Command *command_divide;

	command_add = cbcp_client_init_command(
		cbcp, "Server", "Calculation", "Add");
	command_subtract = cbcp_client_init_command(
		cbcp, "Server", "Calculation", "Subtract");
	command_multiply = cbcp_client_init_command(
		cbcp, "Server", "Calculation", "Multiply");
	command_divide = cbcp_client_init_command(
		cbcp, "Server", "Calculation", "Divide");

	/*
	** Client code here
	*/

	uint32_t a = atoi(argv[2]);
	char operator = *argv[3];
	uint32_t b = atoi(argv[4]);

	CBCP_Command *command_to_execute = NULL;

	switch(operator)
	{
		case '+':
			command_to_execute = command_add;
			break;
		case '-':
			command_to_execute = command_subtract;
			break;
		case '*':
			command_to_execute = command_multiply;
			break;
		case '/':
			command_to_execute = command_divide;
			break;
		default:
			fprintf(stderr, "Unknown operator: '%c'\n", operator);
			return 1;
	}

	if(command_to_execute == NULL)
	{
		fprintf(stderr, "Missing capability to execute command '%c'\n", operator);
		return 1;
	}

	printf(
		"Sending cbcp command '%c' to the server with payload %d; %d.\n",
		operator, a, b);

	uint32_t payload[2] = {0};
	payload[0] = a;
	payload[1] = b;


	response = cbcp_client_send_command(
		command_to_execute,
		(void*)payload,
		sizeof(payload),
		response_buffer,
		sizeof(response_buffer));

	if(response.success)
	{
		printf("Server has received the command.\n");
		if(response.payload != NULL)
		{
			printf("Length of response: %d\n", response.payload_length);
			printf("Response: %d\n", *((uint32_t*)response.payload));
		}
		else
		{
			printf("No payload?\n");
		}
	}
	else
		printf("Sending command to server was not successful!\n");

	return 0;
}

Server Source Code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
** Hello Name Server
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>


/*
** CBCP command predeclarations
*/
static void command_add(CBCP_Command_Args *args);
static void command_subtract(CBCP_Command_Args *args);
static void command_multiply(CBCP_Command_Args *args);
static void command_divide(CBCP_Command_Args *args);


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Bind command callbacks
	*/

	if (!cbcp_server_init_command(
		cbcp,
		"Calculation", "Add", command_add,
		NULL, sizeof(uint32_t))
	) {
		fprintf(stderr, "Could not initialize add command.\n");
		return 1;
	}

	if (!cbcp_server_init_command(
		cbcp,
		"Calculation", "Subtract", command_subtract,
		NULL, sizeof(uint32_t))
	) {
		fprintf(stderr, "Could not initialize subtract command.\n");
		return 1;
	}

	if (!cbcp_server_init_command(
		cbcp,
		"Calculation", "Multiply", command_multiply,
		NULL, sizeof(uint32_t))
	) {
		fprintf(stderr, "Could not initialize multiply command.\n");
		return 1;
	}

	if (!cbcp_server_init_command(
		cbcp,
		"Calculation", "Divide", command_divide,
		NULL, sizeof(uint32_t))
	) {
		fprintf(stderr, "Could not initialize divide command.\n");
		return 1;
	}

	/*
	** Start the cbcp server
	*/

	printf("Starting CBCP server for Hello Name application.\n");

	cbcp_server_start(cbcp);

	return 0;
}



static void command_add(CBCP_Command_Args *args) {
	uint32_t *message = (uint32_t *)args->payload;
	unsigned int message_length = args->payload_length;
	if(message_length != sizeof(uint32_t)*2)
	{
		fprintf(stderr, "message_length is not equal to two 'uint32's!\n"
		                "\tmessage_length: %d\n", message_length);
		return;
	}
	char *response_payload = (char *)args->response_payload;
	unsigned int max_payload_length = args->response_payload_max_length;
	if(max_payload_length < sizeof(uint32_t))
	{
		fprintf(stderr, "max_payload_length is too small!\n");
		return;
	}

	uint32_t a = message[0];
	uint32_t b = message[1];
	uint32_t result = a + b;

	printf(
		"Adding %d and %d. result: %d!\n",
		a, b, result);

	args->response_payload_length = sizeof(uint32_t);
	memcpy(response_payload, (void*)&result, sizeof(result));
}


static void command_subtract(CBCP_Command_Args *args) {
	uint32_t *message = (uint32_t *)args->payload;
	unsigned int message_length = args->payload_length;
	if(message_length != sizeof(uint32_t)*2)
	{
		fprintf(stderr, "message_length is not equal to two 'uint32's!\n"
		                "\tmessage_length: %d\n", message_length);
		return;
	}
	char *response_payload = (char *)args->response_payload;
	unsigned int max_payload_length = args->response_payload_max_length;
	if(max_payload_length < sizeof(uint32_t))
	{
		fprintf(stderr, "max_payload_length is too small!\n");
		return;
	}

	uint32_t a = message[0];
	uint32_t b = message[1];
	uint32_t result = a - b;

	printf(
		"Adding %d and %d. result: %d!\n",
		a, b, result);

	args->response_payload_length = sizeof(uint32_t);
	memcpy(response_payload, (void*)&result, sizeof(result));
}


static void command_multiply(CBCP_Command_Args *args) {
	uint32_t *message = (uint32_t *)args->payload;
	unsigned int message_length = args->payload_length;
	if(message_length != sizeof(uint32_t)*2)
	{
		fprintf(stderr, "message_length is not equal to two 'uint32's!\n"
		                "\tmessage_length: %d\n", message_length);
		return;
	}
	char *response_payload = (char *)args->response_payload;
	unsigned int max_payload_length = args->response_payload_max_length;
	if(max_payload_length < sizeof(uint32_t))
	{
		fprintf(stderr, "max_payload_length is too small!\n");
		return;
	}

	uint32_t a = message[0];
	uint32_t b = message[1];
	uint32_t result = a * b;

	printf(
		"Adding %d and %d. result: %d!\n",
		a, b, result);

	args->response_payload_length = sizeof(uint32_t);
	memcpy(response_payload, (void*)&result, sizeof(result));
}


static void command_divide(CBCP_Command_Args *args) {
	uint32_t *message = (uint32_t *)args->payload;
	unsigned int message_length = args->payload_length;
	if(message_length != sizeof(uint32_t)*2)
	{
		fprintf(stderr, "message_length is not equal to two 'uint32's!\n"
		                "\tmessage_length: %d\n", message_length);
		return;
	}
	char *response_payload = (char *)args->response_payload;
	unsigned int max_payload_length = args->response_payload_max_length;
	if(max_payload_length < sizeof(uint32_t))
	{
		fprintf(stderr, "max_payload_length is too small!\n");
		return;
	}

	uint32_t a = message[0];
	uint32_t b = message[1];
	uint32_t result = 0;
	if(b != 0)
		result = a / b;

	printf(
		"Adding %d and %d. result: %d!\n",
		a, b, result);

	args->response_payload_length = sizeof(uint32_t);
	memcpy(response_payload, (void*)&result, sizeof(result));
}
Jump back to top

CBCP Example 6: User Data

This tutorial focuses on user data. The user data is data you can associate with a callback that is available though its args struct. You can use this to persist data between calls and even between different callbacks. It is up to you how you use the user data.

In the following example code we will be storing an integer that is accessed by a single callback. We will be using example 3 as a basis, except we remove the client to server data. Instead, the server keeps a counter of the number of times the callback has been called, which is reported to the client.

The network we use in this example is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
!CBCP 1.0

!HOSTS
Client ; TCP,127.0.0.1:9000
Server ; TCP,127.0.0.1:9002

!INTERFACES
Hello ; Hello

!IMPLEMENTS
Server ; Hello

!CAPABILITIES
Client ; Server ; Hello ; Hello

Client

The client is like the one from example 3 except that the client to server data has been removed. For details, see the full client source code.

Server

We create a counter in the main function of the server. By placing it in the outer scope we are assured that it has program lifetime, which is sufficient for our example. To pass it as user data to the callback we simply pass it to the binding.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/*
** Bind command callbacks
*/

// This has program lifetime because it is in the outer scope of main.
uint32_t hello_counter = 0;

if (!cbcp_server_init_command(
	cbcp,
	"Hello",
	"Hello",
	command_hello,
	(void*)&hello_counter,
	64)
) {
	fprintf(stderr, "Could not initialize served command.\n");
	return 1;
}

In the callback we can now access the counter through the user_data field of the args struct. When we read and write the user data as we find appropriate. In this case we count it up and we use it when we generate our response string.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
static void command_hello(CBCP_Command_Args *args) {
	uint32_t *hello_counter = (uint32_t*)args->user_data;
	*hello_counter += 1;

	char *response_payload = (char *)args->response_payload;
	int max_payload_length = args->response_payload_max_length;
	int string_size = snprintf(
		response_payload,
		max_payload_length,
		"Hello, you are client number %d to greet me!",
		*hello_counter);
	if(string_size < max_payload_length)
		args->response_payload_length = string_size;
	else
		args->response_payload_length = max_payload_length;
	printf(
		"Hello number %d was received! Returning the greeting!\n",
		*hello_counter);
}

Client Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
** Hello Name Client
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>
#include <string.h>


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Init commands
	*/

	CBCP_Command *command_hello_world;
	command_hello_world = cbcp_client_init_command(
		cbcp,
		"Server",
		"Hello",
		"Hello");

	if (!command_hello_world) {
		fprintf(stderr, "Could not initialize required command.\n");
		return 1;
	}

	/*
	** Client code here
	*/

	printf("Sending cbcp command to the server.\n");

	CBCP_Response response;
	char response_buffer[4096];

	response = cbcp_client_send_command(
		command_hello_world,
		NULL,
		0,
		response_buffer,
		sizeof(response_buffer));

	if(response.success)
	{
		printf("Server has received the command.\n");
		if(response.payload != NULL)
		{
			printf("Length of response: %d\n", response.payload_length);
			printf(
				"Response: %.*s\n",
				response.payload_length,
				(char*)response.payload);
		}
		else
		{
			printf("No payload?\n");
		}
	}
	else
		printf("Sending command to server was not successful!\n");

	return 0;
}

Server Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
** Hello Name Server
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>


/*
** CBCP command predeclarations
*/
static void command_hello(CBCP_Command_Args *args);


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Bind command callbacks
	*/

	// This has program lifetime because it is in the outer scope of main.
	uint32_t hello_counter = 0;

	if (!cbcp_server_init_command(
		cbcp,
		"Hello",
		"Hello",
		command_hello,
		(void*)&hello_counter,
		64)
	) {
		fprintf(stderr, "Could not initialize served command.\n");
		return 1;
	}

	/*
	** Start the cbcp server
	*/

	printf("Starting CBCP server for Hello Name application.\n");

	cbcp_server_start(cbcp);

	return 0;
}

static void command_hello(CBCP_Command_Args *args) {
	uint32_t *hello_counter = (uint32_t*)args->user_data;
	*hello_counter += 1;

	char *response_payload = (char *)args->response_payload;
	int max_payload_length = args->response_payload_max_length;
	int string_size = snprintf(
		response_payload,
		max_payload_length,
		"Hello, you are client number %d to greet me!",
		*hello_counter);
	if(string_size < max_payload_length)
		args->response_payload_length = string_size;
	else
		args->response_payload_length = max_payload_length;
	printf(
		"Hello number %d was received! Returning the greeting!\n",
		*hello_counter);
}
Jump back to top

CBCP Example 7: Rejection

If a command gets rejected you might want to be notified, e.g. for logging purposes. You can bind a callback for rejection so that you can respond appropriately to command attempts that fail verification.

The network we use in this example is shown below. There is no client in this example, as we will trigger the callback by sending garbage to the server using netcat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
!CBCP 1.0

!HOSTS
Server ; TCP,127.0.0.1:9002

!INTERFACES
Interface ; Command

!IMPLEMENTS
Server ; Interface

!CAPABILITIES

Fake Client

The fake client that we will be using is a shell script that simply writes some arbitrary data to the server using netcat. Because this data is malformed from a CBCP viewpoint, the command will get rejected and the callback will get triggered on the server.

1
2
#!/bin/bash
echo "asdf" | netcat 127.0.0.1 9002

Server

We bind a single rejection callback for the entire server. The NULL in the parameters is a user data pointer, which is something you can read more about in tutorial 6.

1
2
3
4
5
6
7
8
/*
** Set rejection callback
*/

cbcp_server_set_command_rejected_callback(
	cbcp,
	command_rejected_callback,
	NULL);

The rejection callback is very simple. We simply print that the rejection happened along with the reason, but anything can be done here.

1
2
3
static void command_rejected_callback(CBCP_Command_Rejected_Args args) {
	fprintf(stderr, "Command rejected! Reason: %x\n", args.reason);
}

Client Source Code

1
2
#!/bin/bash
echo "asdf" | netcat 127.0.0.1 9002

Server Source Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/*
** Hello World Server
*/

#include <cbcp.h>
#define CBCP_NET_TCP_IMPLEMENTATION
#include <cbcp_net_tcp.h>

#include <stdio.h>


/*
** CBCP command predeclarations
*/
static void command(CBCP_Command_Args *args);

static void command_rejected_callback(CBCP_Command_Rejected_Args args);


/*
** CBCP program
*/
int main(int argc, char const *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "Usage:\n\t%s <cbcp_database_filepath>\n", argv[0]);
		return 1;
	}

	/*
	** Initialize CBCP by loading the database file for this host
	*/

	const char *cbcp_database_filepath = argv[1];
	CBCP_State *cbcp;

	cbcp_net_tcp_init(0);
	cbcp = cbcp_init(cbcp_database_filepath);

	if (cbcp == NULL) {
		fprintf(stderr, "Could not initialize CBCP.\n");
		return 1;
	}

	/*
	** Bind command callbacks
	*/

	if (!cbcp_server_init_command(
		cbcp,
		"Interface",
		"Command",
		command,
		NULL,
		0)
	) {
		fprintf(stderr, "Could not initialize served command.\n");
		return 1;
	}

	/*
	** Set rejection callback
	*/

	cbcp_server_set_command_rejected_callback(
		cbcp,
		command_rejected_callback,
		NULL);

	/*
	** Start the cbcp server
	*/

	printf("Starting CBCP server for Hello World application.\n");

	cbcp_server_start(cbcp);

	return 0;
}

static void command(CBCP_Command_Args *args) {
	(void)args;
	printf("Command executed.\n");
}

static void command_rejected_callback(CBCP_Command_Rejected_Args args) {
	fprintf(stderr, "Command rejected! Reason: %x\n", args.reason);
}