protomsg¶ ↑
Protocol Message Buffers for C. This gem generates C socket code for reading and writing messages described in the protomsg DSL. The code is highly optimised and often similar to what you would write by hand manually.
Overview¶ ↑
Input for a ‘request’ message:
request { int type string key raw data }
Output is a header file of macros for reading, writing and manipulating request messages:
// attribute getters get_request_type get_request_key get_request_data // attribute setters set_request_type set_request_key set_request_data // lengths request_key_length request_data_length request_length // memory & IO init_request free_request write_request read_request
The macros generate highly optimised copy free socket code (using scatter/gather IO functions), and automatically check message sanity and handle incomplete reads and writes. The generated code is often very similar to what you would write by hand, and in some cases is even faster.
Usage¶ ↑
Call the protomsg script with the message types input file.
$ protomsg mymessages.protomsg Created protomsg.h Created request_message.h
Each file may describe multiple message types. The format for each message type is:
message_type_name { attribute_type attribute_name ... }
All message type and attribute names must be valid C identifiers. Valid attribute types are:
int 64 bit signed integer float 64 bit float string C string raw Raw bytes
Example¶ ↑
Once you have defined your message types, sending and receiving messages is easy. For example, given the following message type definitions:
# ping_message.protomsg ping { int number string message } response { int code }
Use the protomsg script:
$ protomsg ping_message.protomsg Created protomsg.h Created ping_message.h Creates response_message.h
A number of utility macros are defined that make it easy to write clients and servers. An example client could be written as:
#include "response_message.h" #include "ping_message.h" int main(void) { int error, server; response *r; ping *p; // create the ping message init_ping(p); set_ping_number(p, 1); set_ping_message(p, "Hello World!"); // connect to the server and send the message connect_to_server("localhost", 9000, server, error); write_ping(p, server, error); // read a response message read_response(r, server, error); printf("Response code was: %llu\n", get_response_code(r)); }
And the corresponding server could be:
#include "response_message.h" #include "ping_message.h" int main(void) { int error, server, client; response *r; ping *p; // start a server and wait for a ping message create_server_socket(9000, 1, server, error); accept_client(server, client, error); read_ping(p, client, error); printf("Received ping: %llu, %s\n", get_ping_number(p), get_ping_message(p)); // create a response message and send init_response(r); set_response_code(r, 1); write_response(r, client, error); }
It’s not necessary to use the included socket helper macros - you can use proto messages over any socket - but for simple client/server applications like this they help reduce the amount of code required to get something running quickly.
Known Issues¶ ↑
All clients are servers are assumed to be on machines of the same endianess. The simple socket creation macros can’t create IP6 sockets as yet. Writev is used to send messages; an error is currently raised when an incomplete write completes, rather than restart the write where it was left off.