protomsg 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +69 -1
- data/lib/templates/getters/string_getter.h +1 -1
- data/lib/templates/io.h +9 -4
- data/lib/templates/lengths.h +1 -1
- data/lib/templates/message_type.h +5 -0
- data/lib/templates/protomsg.h +63 -7
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Protocol Message Buffers for C. This gem generates C socket code for reading and writing messages described in the protomsg DSL.
|
4
4
|
|
5
|
-
==
|
5
|
+
== Overview
|
6
6
|
Input for a 'request' message:
|
7
7
|
|
8
8
|
request {
|
@@ -57,5 +57,73 @@ All message type and attribute names must be valid C identifiers. Valid attribut
|
|
57
57
|
string C string
|
58
58
|
raw Raw bytes
|
59
59
|
|
60
|
+
== Example
|
61
|
+
Once you have defined your message types, sending and receiving messages is easy. For example, given the following message type:
|
62
|
+
|
63
|
+
# ping_message.protomsg
|
64
|
+
ping {
|
65
|
+
int number
|
66
|
+
string message
|
67
|
+
}
|
68
|
+
|
69
|
+
response {
|
70
|
+
int code
|
71
|
+
}
|
72
|
+
|
73
|
+
Use the protomsg script:
|
74
|
+
|
75
|
+
$ protomsg ping_message.protomsg
|
76
|
+
Created protomsg.h
|
77
|
+
Created ping_message.h
|
78
|
+
Creates response_message.h
|
79
|
+
|
80
|
+
A number of utility macros are defined that make it easy to write clients and servers. An example client could be written as:
|
81
|
+
|
82
|
+
#include "response_message.h"
|
83
|
+
#include "ping_message.h"
|
84
|
+
|
85
|
+
int main(void) {
|
86
|
+
int error, server;
|
87
|
+
response *r;
|
88
|
+
ping *p;
|
89
|
+
|
90
|
+
// create the ping message
|
91
|
+
init_ping(p);
|
92
|
+
set_ping_number(p, 1);
|
93
|
+
set_ping_message(p, "Hello World!");
|
94
|
+
|
95
|
+
// connect to the server and send the message
|
96
|
+
connect_to_server("localhost", 9000, server, error);
|
97
|
+
write_ping(p, server, error);
|
98
|
+
|
99
|
+
// read a response message
|
100
|
+
read_response(r, server, error);
|
101
|
+
printf("Response code was: %llu\n", get_response_code(r));
|
102
|
+
}
|
103
|
+
|
104
|
+
And the corresponding server could be:
|
105
|
+
|
106
|
+
#include "response_message.h"
|
107
|
+
#include "ping_message.h"
|
108
|
+
|
109
|
+
int main(void) {
|
110
|
+
int error, server, client;
|
111
|
+
response *r;
|
112
|
+
ping *p;
|
113
|
+
|
114
|
+
// start a server and wait for a ping message
|
115
|
+
create_server_socket(9000, 1, server, error);
|
116
|
+
accept_client(server, client);
|
117
|
+
read_ping(p, client, error);
|
118
|
+
printf("Received ping: %llu, %s\n", get_ping_number(p), get_ping_message(p));
|
119
|
+
|
120
|
+
// create a response message and send
|
121
|
+
init_response(r);
|
122
|
+
set_response_code(r, 1);
|
123
|
+
write_response(r, client, error);
|
124
|
+
}
|
125
|
+
|
126
|
+
It's not necessary to use the included socket helper macros - you can use protomsg's over any socket - but for simple client/server applications like this they help reduce the amount of code required to get something running quickly.
|
127
|
+
|
60
128
|
== Known Issues
|
61
129
|
All clients are servers are assumed to be on machines of the same endianess.
|
@@ -1 +1 @@
|
|
1
|
-
#define get_<%= @message_type.name %>_<%= @name %>(msg) (msg-><%= @name %>)
|
1
|
+
#define get_<%= @message_type.name %>_<%= @name %>(msg) ((char *)msg-><%= @name %>)
|
data/lib/templates/io.h
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#define write_<%= @name %>(msg, sock, error) {\
|
2
|
+
error = 0;\
|
2
3
|
ssize_t written = writev(sock, (const struct iovec *)msg, <%= variable_attributes.size + 1 %>);\
|
3
4
|
if(written != <%= @name %>_length(msg)){\
|
4
5
|
error = errno;\
|
@@ -10,9 +11,13 @@
|
|
10
11
|
msg->header_length = sizeof(<%= @name %>_header);\
|
11
12
|
msg->header = (<%= @name %>_header *)malloc(sizeof(<%= @name %>_header));\
|
12
13
|
read_struct(sock, msg->header, sizeof(<%= @name %>_header), error);\
|
13
|
-
if(error
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
if(!error) {\
|
15
|
+
if(msg->header->_version == PROTO_MESSAGE_VERSION && msg->header->_type == <%= @name.upcase %>_MESSAGE_TYPE) {\
|
16
|
+
<% variable_attributes.each do |attr| %>msg-><%= attr.name %>_length = msg->header-><%= attr.name %>_length;\
|
17
|
+
msg-><%= attr.name %> = malloc(msg->header-><%= attr.name %>_length);\
|
18
|
+
<% end %>readv(sock, (const struct iovec *)msg + 1, <%= variable_attributes.size %>);\
|
19
|
+
} else {\
|
20
|
+
error = INVALID_PROTOMSG_HEADER;\
|
21
|
+
}\
|
17
22
|
}\
|
18
23
|
}
|
data/lib/templates/lengths.h
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
<% variable_attributes.each do |attr| %>#define <%= @name %>_<%= attr.name %>_length(msg) (msg-><%= attr.name %>_length)
|
2
|
-
<% end %>#define <%= @name %>_length(msg) (msg->header_length +
|
2
|
+
<% end %>#define <%= @name %>_length(msg) (msg->header_length <%= variable_attributes.size > 0 ? ("+ " + variable_attributes.collect {|attr| "msg->#{attr.name}_length"}.join(" + ")) : '' %>)
|
@@ -9,6 +9,9 @@
|
|
9
9
|
#include <unistd.h>
|
10
10
|
#include "protomsg.h"
|
11
11
|
|
12
|
+
#ifndef __protomsg_<%= @name %>__
|
13
|
+
#define __protomsg_<%= @name %>__
|
14
|
+
|
12
15
|
#define <%= @name.upcase %>_MESSAGE_TYPE <%= @type_number %>
|
13
16
|
|
14
17
|
<%= header %>
|
@@ -23,3 +26,5 @@
|
|
23
26
|
<%= memory %>
|
24
27
|
// io
|
25
28
|
<%= io %>
|
29
|
+
|
30
|
+
#endif
|
data/lib/templates/protomsg.h
CHANGED
@@ -1,21 +1,77 @@
|
|
1
|
+
#include <sys/socket.h>
|
2
|
+
#include <sys/types.h>
|
3
|
+
#include <arpa/inet.h>
|
4
|
+
#include <netdb.h>
|
1
5
|
#include <stdio.h>
|
6
|
+
#include <errno.h>
|
2
7
|
|
3
8
|
#ifndef __protomsg__
|
4
9
|
#define __protomsg__
|
5
10
|
|
6
|
-
#define PROTO_MESSAGE_VERSION
|
11
|
+
#define PROTO_MESSAGE_VERSION 1
|
12
|
+
#define INVALID_PROTOMSG_HEADER 1
|
13
|
+
|
7
14
|
#define read_struct(sock, structure, size, error) {\
|
8
|
-
ssize_t bytes_read = 0, bytes_total = 0;\
|
9
15
|
error = 0;\
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
16
|
+
ssize_t _bytes_read = 0, _bytes_total = 0;\
|
17
|
+
while(_bytes_total < size) {\
|
18
|
+
_bytes_read = read(sock, structure + _bytes_total, size - _bytes_total);\
|
19
|
+
if(_bytes_read != -1) {\
|
20
|
+
_bytes_total += _bytes_read;\
|
14
21
|
} else {\
|
15
22
|
error = errno;\
|
16
|
-
|
23
|
+
_bytes_total = size;\
|
24
|
+
}\
|
25
|
+
}\
|
26
|
+
}
|
27
|
+
|
28
|
+
#define create_server_socket(port, queue, sock, error) {\
|
29
|
+
error = 0;\
|
30
|
+
sock = socket(AF_INET, SOCK_STREAM, 0);\
|
31
|
+
struct sockaddr_in _servaddr;\
|
32
|
+
memset(&_servaddr, 0, sizeof(_servaddr));\
|
33
|
+
_servaddr.sin_family = AF_INET;\
|
34
|
+
_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);\
|
35
|
+
_servaddr.sin_port = htons(port);\
|
36
|
+
error = bind(sock, (const struct sockaddr *)&_servaddr, sizeof(_servaddr));\
|
37
|
+
if(error) {\
|
38
|
+
error = errno;\
|
39
|
+
} else {\
|
40
|
+
error = listen(sock, queue);\
|
41
|
+
if(error) {\
|
42
|
+
error = errno;\
|
17
43
|
}\
|
18
44
|
}\
|
19
45
|
}
|
20
46
|
|
47
|
+
#define accept_client(sock, client) {\
|
48
|
+
client = accept(sock, NULL, NULL);\
|
49
|
+
}
|
50
|
+
|
51
|
+
#define connect_to_server(address, port, sock, error) {\
|
52
|
+
error = 0;\
|
53
|
+
sock = socket(AF_INET, SOCK_STREAM, 0);\
|
54
|
+
struct sockaddr _servaddr;\
|
55
|
+
socklen_t _servaddr_length;\
|
56
|
+
get_socket_addr_from_hostname(address, _servaddr, _servaddr_length, error);\
|
57
|
+
if(!error) {\
|
58
|
+
((struct sockaddr_in *) &_servaddr)->sin_port = htons(port);\
|
59
|
+
error = connect(sock, (struct sockaddr *) &_servaddr, sizeof(_servaddr));\
|
60
|
+
}\
|
61
|
+
}
|
62
|
+
|
63
|
+
#define get_socket_addr_from_hostname(host, servaddr, length, error) {\
|
64
|
+
struct addrinfo _hints, *_result;\
|
65
|
+
memset(&_hints, 0, sizeof(_hints));\
|
66
|
+
_hints.ai_family = AF_INET;\
|
67
|
+
_hints.ai_socktype = SOCK_STREAM;\
|
68
|
+
_hints.ai_protocol = IPPROTO_TCP;\
|
69
|
+
error = getaddrinfo(host, NULL, &_hints, &_result);\
|
70
|
+
if(!error) {\
|
71
|
+
length = _result->ai_addrlen;\
|
72
|
+
memcpy(&servaddr, _result->ai_addr, length);\
|
73
|
+
freeaddrinfo(_result);\
|
74
|
+
}\
|
75
|
+
}
|
76
|
+
|
21
77
|
#endif
|