mirror of
https://github.com/protobuf-c/protobuf-c.git
synced 2024-12-29 07:19:42 +08:00
initial commit. this is NOT WORKING by a long shot.
git-svn-id: https://protobuf-c.googlecode.com/svn/trunk@104 00440858-1255-0410-a3e6-75ea37f81c3a
This commit is contained in:
parent
ddb111cab3
commit
27aed0b2ac
98
src/sctp-rpc/example-client.c
Normal file
98
src/sctp-rpc/example-client.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#include "example-word-funcs-service.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
word_closure_to_ptr_word (const Example__Word *message,
|
||||||
|
void *closure_data)
|
||||||
|
{
|
||||||
|
const char *rv = message == NULL || message->word == NULL
|
||||||
|
? "*error*" : message->word;
|
||||||
|
* (char **) closure_data = strdup (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
Example__WordFuncs_Service *service = NULL;
|
||||||
|
const char *name = "word_funcs";
|
||||||
|
const char *addr = NULL;
|
||||||
|
|
||||||
|
dispatch = protobuf_c_sctp_dispatch_new ();
|
||||||
|
|
||||||
|
/* Create service (either local or remote) */
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (strcmp (argv[i], "--local") == 0)
|
||||||
|
{
|
||||||
|
example_word_funcs_service_init ();
|
||||||
|
service = example_word_funcs_service;
|
||||||
|
name = "<local word_funcs>";
|
||||||
|
}
|
||||||
|
else if (strncmp (argv[i], "--addr=", 7) == 0)
|
||||||
|
addr = argv[i] + 7;
|
||||||
|
else if (strncmp (argv[i], "--name=", 7) == 0)
|
||||||
|
name = argv[i] + 7;
|
||||||
|
else
|
||||||
|
usage (argv[0]);
|
||||||
|
}
|
||||||
|
if (service == NULL && addr == NULL)
|
||||||
|
die ("missing --addr=");
|
||||||
|
if (service == NULL && addr != NULL)
|
||||||
|
{
|
||||||
|
channel = protobuf_c_sctp_channel_ipv4_connect_by_hostport (addr, dispatch);
|
||||||
|
service = protobuf_c_sctp_channel_add_remote_service (channel,
|
||||||
|
name,
|
||||||
|
&example__word_funcs__descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* main loop: invoke service methods (either local or remote) */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s >> ", name);
|
||||||
|
if (fgets (buf, sizeof (buf), stdin) == NULL)
|
||||||
|
break;
|
||||||
|
/* parse command */
|
||||||
|
at = buf;
|
||||||
|
while (*at && isspace (*at))
|
||||||
|
at++;
|
||||||
|
if (*at == 0)
|
||||||
|
continue;
|
||||||
|
cmd = at;
|
||||||
|
while (*at && !isspace (*at))
|
||||||
|
at++;
|
||||||
|
if (*at != 0)
|
||||||
|
{
|
||||||
|
*at++ = 0;
|
||||||
|
while (*at && *isspace (*at))
|
||||||
|
at++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp (cmd, "uppercase") == 0
|
||||||
|
|| strcmp (cmd, "lowercase") == 0)
|
||||||
|
{
|
||||||
|
/* functions mapping string => string can be handled via this
|
||||||
|
mechanism */
|
||||||
|
char *rv = NULL;
|
||||||
|
Example__Word input = EXAMPLE__WORD__INIT;
|
||||||
|
input.word = at;
|
||||||
|
if (strcmp (cmd, "uppercase") == 0)
|
||||||
|
example__word_funcs__uppercase (service,
|
||||||
|
&input,
|
||||||
|
word_closure_to_ptr_word,
|
||||||
|
&rv);
|
||||||
|
else if (strcmp (cmd, "lowercase") == 0)
|
||||||
|
example__word_funcs__lowercase (service,
|
||||||
|
&input,
|
||||||
|
word_closure_to_ptr_word,
|
||||||
|
&rv);
|
||||||
|
else
|
||||||
|
assert (0);
|
||||||
|
while (rv == NULL)
|
||||||
|
protobuf_c_sctp_dispatch_run (dispatch);
|
||||||
|
|
||||||
|
printf ("%s\n", rv);
|
||||||
|
fflush (stdout);
|
||||||
|
free (rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
33
src/sctp-rpc/example-server-2.c
Normal file
33
src/sctp-rpc/example-server-2.c
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "protobuf-c-sctp.h"
|
||||||
|
|
||||||
|
void usage (const char *prog_name)
|
||||||
|
{
|
||||||
|
die ("usage: %s --port=PORT [--name=NAME]\n"
|
||||||
|
"Run an example word-funcs service.\n", prog_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
int port = 0;
|
||||||
|
ProtobufC_SCTP_LocalService local_services[1] = { { "word_funcs", NULL } };
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (strncmp (argv[i], "--port=", 7) == 0)
|
||||||
|
port = atoi (argv[i] + 7);
|
||||||
|
else if (strncmp (argv[i], "--name=", 7) == 0)
|
||||||
|
local_services[0].name = argv[i] + 7;
|
||||||
|
else
|
||||||
|
usage (argv[0]);
|
||||||
|
}
|
||||||
|
if (port == 0)
|
||||||
|
die ("--port=PORT is required");
|
||||||
|
|
||||||
|
/* initialize our our actual service implementation */
|
||||||
|
example_word_funcs_service_init ();
|
||||||
|
local_services[0].service = example_word_funcs_service;
|
||||||
|
|
||||||
|
/* run the server */
|
||||||
|
protobuf_c_sctp_server_run (1, local_services, 0, NULL, port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
60
src/sctp-rpc/example-server.c
Normal file
60
src/sctp-rpc/example-server.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#include "protobuf-c-sctp.h"
|
||||||
|
|
||||||
|
typedef struct _StringFuncsImpl StringFuncsImpl;
|
||||||
|
struct _StringFuncsImpl
|
||||||
|
{
|
||||||
|
Example__WordFuncs_Service base_service;
|
||||||
|
};
|
||||||
|
|
||||||
|
void usage (const char *prog_name)
|
||||||
|
{
|
||||||
|
die ("usage: %s --port=PORT [--name=NAME]\n"
|
||||||
|
"Run an example word-funcs service.\n", prog_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _ChannelConfigData ChannelConfigData;
|
||||||
|
struct _ChannelConfigData
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
ProtobufCService *service;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Set up newly-accepted connection */
|
||||||
|
static void
|
||||||
|
init_channel (ProtobufC_SCTP_Channel *channel,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
ChannelConfigData *config = data;
|
||||||
|
protobuf_c_sctp_channel_add_local_service (channel,
|
||||||
|
config->name,
|
||||||
|
config->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
int port = 0;
|
||||||
|
StringFuncsImpl impl;
|
||||||
|
ChannelConfigData config = { "word_funcs", &impl.base_service.base };
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (strncmp (argv[i], "--port=", 7) == 0)
|
||||||
|
port = atoi (argv[i] + 7);
|
||||||
|
else if (strncmp (argv[i], "--name=", 7) == 0)
|
||||||
|
config.name = argv[i] + 7;
|
||||||
|
else
|
||||||
|
usage (argv[0]);
|
||||||
|
}
|
||||||
|
if (port == 0)
|
||||||
|
die ("--port=PORT is required");
|
||||||
|
|
||||||
|
/* initialize our our actual service implementation */
|
||||||
|
example__word_funcs__init (&impl.base_service);
|
||||||
|
impl.base_service.uppercase = implement_uppercase;
|
||||||
|
|
||||||
|
listener = protobuf_c_sctp_listener_new_ipv4 (NULL, port, dispatch,
|
||||||
|
init_channel, &config);
|
||||||
|
for (;;)
|
||||||
|
protobuf_c_sctp_dispatch_run (dispatch, -1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
61
src/sctp-rpc/example-word-funcs-service.c
Normal file
61
src/sctp-rpc/example-word-funcs-service.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include "example-word-funcs-service.h"
|
||||||
|
|
||||||
|
/* note: the implementations here only handle ascii characters--
|
||||||
|
this is a tutorial about RPC,
|
||||||
|
not utf8 encoding or unicode normalization */
|
||||||
|
|
||||||
|
Example__WordFuncs_Service *example_word_funcs_service;
|
||||||
|
|
||||||
|
static Example__WordFuncs_Service word_funcs_service;
|
||||||
|
|
||||||
|
static void
|
||||||
|
implement_uppercase (Example__WordFuncs_Service *service,
|
||||||
|
const Example__Word *input,
|
||||||
|
Example__Word_Closure closure,
|
||||||
|
void *closure_data)
|
||||||
|
{
|
||||||
|
char *rv = strdup (input->word);
|
||||||
|
Example__Word output = EXAMPLE__WORD__INIT;
|
||||||
|
char *at;
|
||||||
|
for (at = rv; *at; at++)
|
||||||
|
if ('a' <= *at && *at <= 'z')
|
||||||
|
*at -= ('a' - 'A');
|
||||||
|
output.word = rv;
|
||||||
|
closure (&output, closure_data);
|
||||||
|
free (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
implement_lowercase (Example__WordFuncs_Service *service,
|
||||||
|
const Example__Word *input,
|
||||||
|
Example__Word_Closure closure,
|
||||||
|
void *closure_data)
|
||||||
|
{
|
||||||
|
char *rv = strdup (input->word);
|
||||||
|
Example__Word output = EXAMPLE__WORD__INIT;
|
||||||
|
char *at;
|
||||||
|
for (at = rv; *at; at++)
|
||||||
|
if ('A' <= *at && *at <= 'Z')
|
||||||
|
*at += ('a' - 'A');
|
||||||
|
output.word = rv;
|
||||||
|
closure (&output, closure_data);
|
||||||
|
free (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 /* you could consider an init function like this instead */
|
||||||
|
void example_word_funcs_service_init (void)
|
||||||
|
{
|
||||||
|
example_word_funcs_service = &word_funcs_service;
|
||||||
|
example__word_funcs__init (&word_funcs_service);
|
||||||
|
word_funcs_service.uppercase = implement_uppercase;
|
||||||
|
word_funcs_service.lowercase = implement_lowercase;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* TODO: provide a c99 example */
|
||||||
|
Example__WordFuncs_Service example__word_funcs__service__global =
|
||||||
|
{
|
||||||
|
EXAMPLE__WORD_FUNCS__SERVICE__BASE_INIT,
|
||||||
|
implement_uppercase,
|
||||||
|
implement_lowercase
|
||||||
|
};
|
8
src/sctp-rpc/example-word-funcs-service.h
Normal file
8
src/sctp-rpc/example-word-funcs-service.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "generated/example.pb-c.h"
|
||||||
|
|
||||||
|
extern Example__WordFuncs_Service example__word_funcs__service__global;
|
||||||
|
|
||||||
|
/* extern ProtobufCService *example__word_funcs__service; */
|
||||||
|
#define example__word_funcs__service \
|
||||||
|
((ProtobufCService *) (&example__word_funcs__service__global))
|
||||||
|
|
11
src/sctp-rpc/example.proto
Normal file
11
src/sctp-rpc/example.proto
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package example;
|
||||||
|
|
||||||
|
message Word
|
||||||
|
{
|
||||||
|
required string word = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
service WordFuncs {
|
||||||
|
rpc Uppercase (Word) returns (Word);
|
||||||
|
rpc Lowercase (Word) returns (Word);
|
||||||
|
}
|
65
src/sctp-rpc/protobuf-c-sctp.h
Normal file
65
src/sctp-rpc/protobuf-c-sctp.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#include "../google/protobuf-c/protobuf-c.h"
|
||||||
|
|
||||||
|
typedef struct _ProtobufC_SCTP_Dispatch ProtobufC_SCTP_Dispatch;
|
||||||
|
typedef struct _ProtobufC_SCTP_Channel ProtobufC_SCTP_Channel;
|
||||||
|
typedef struct _ProtobufC_SCTP_Listener ProtobufC_SCTP_Listener;
|
||||||
|
|
||||||
|
/* For servers and clients, you can get at the array of
|
||||||
|
remote service objects from the closure-data of your local-service's
|
||||||
|
invocation method. */
|
||||||
|
ProtobufCService **
|
||||||
|
protobuf_c_sctp_closure_data_get_remote_services (void *closure_data);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
ProtobufCService *service;
|
||||||
|
} ProtobufC_SCTP_LocalService;
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
ProtobufCServiceDescriptor *descriptor;
|
||||||
|
} ProtobufC_SCTP_RemoteService;
|
||||||
|
|
||||||
|
ProtobufC_SCTP_Config *
|
||||||
|
protobuf_c_sctp_config_new (size_t n_local_services,
|
||||||
|
ProtobufC_SCTP_LocalService *local_services,
|
||||||
|
size_t n_remote_services,
|
||||||
|
ProtobufC_SCTP_RemoteService *remote_services);
|
||||||
|
|
||||||
|
/* a channel: an sctp association (technically sctp allows multiple
|
||||||
|
streams within a single association (unlike tcp), but we do not
|
||||||
|
use that ability. when created as a client, this does automatic
|
||||||
|
reconnecting. */
|
||||||
|
ProtobufC_SCTP_Channel *
|
||||||
|
protobuf_c_sctp_client_new_ipv4(const uint8_t *addr,
|
||||||
|
uint16_t port,
|
||||||
|
ProtobufC_SCTP_Config *config,
|
||||||
|
ProtobufC_Dispatch *dispatch);
|
||||||
|
ProtobufC_SCTP_Channel *
|
||||||
|
protobuf_c_sctp_client_new_ipv4_dns (const char *host,
|
||||||
|
uint16_t port,
|
||||||
|
ProtobufC_SCTP_Config *config,
|
||||||
|
ProtobufC_Dispatch *dispatch);
|
||||||
|
ProtobufC_SCTP_Channel *
|
||||||
|
protobuf_c_sctp_client_new_ipv4_hostport(const char *host_port,
|
||||||
|
ProtobufC_SCTP_Config *config,
|
||||||
|
ProtobufC_SCTP_Dispatch *dispatch);
|
||||||
|
void protobuf_c_sctp_channel_set_error_handler (ProtobufC_SCTP_Channel *channel,
|
||||||
|
ProtobufC_SCTP_ErrorFunc func,
|
||||||
|
void *data,
|
||||||
|
ProtobufC_SCTP_Destroy destroy);
|
||||||
|
ProtobufCService *
|
||||||
|
protobuf_c_sctp_channel_peek_remote_service(ProtobufC_SCTP_Channel *channel,
|
||||||
|
unsigned index);
|
||||||
|
void protobuf_c_sctp_channel_shutdown (ProtobufC_SCTP_Channel *channel);
|
||||||
|
void protobuf_c_sctp_channel_destroy (ProtobufC_SCTP_Channel *channel);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* a listener: a passive sctp socket awaiting new connections */
|
||||||
|
ProtobufC_SCTP_Server *
|
||||||
|
protobuf_c_sctp_server_new_ipv4 (const uint8_t *bind_addr,
|
||||||
|
uint16_t port,
|
||||||
|
ProtobufC_SCTP_Config *config);
|
||||||
|
void protobuf_c_sctp_server_destroy (ProtobufC_SCTP_Server *server);
|
||||||
|
|
||||||
|
|
12
src/sctp-rpc/render-rfc
Executable file
12
src/sctp-rpc/render-rfc
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
nroff -t -ms rfc-protobuf-sctp.ms |
|
||||||
|
perl -e '
|
||||||
|
undef $/; # Read whole files in a single gulp.
|
||||||
|
while (<>) { # Read the entire input file.
|
||||||
|
s/FORMFEED(\[Page\s+\d+\])\s+/ \1\n\f\n/sg;
|
||||||
|
s/\f\n$//; # Want no formfeed at end?
|
||||||
|
print; # Print the resultant file.
|
||||||
|
}' > rfc-protobuf-sctp.txt
|
||||||
|
|
||||||
|
groff -t -ms rfc-protobuf-sctp.ms > rfc-protobuf-sctp.ps
|
371
src/sctp-rpc/rfc-protobuf-sctp.ms
Normal file
371
src/sctp-rpc/rfc-protobuf-sctp.ms
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
.pl 10.0i
|
||||||
|
.po 0
|
||||||
|
.ll 7.2i
|
||||||
|
.lt 7.2i
|
||||||
|
.nr LL 7.2i
|
||||||
|
.nr LT 7.2i
|
||||||
|
.ds LF Benson
|
||||||
|
.ds RF FORMFEED[Page %]
|
||||||
|
.ds CF
|
||||||
|
.ds LH PROPOSAL
|
||||||
|
.ds RH 7 December 2008
|
||||||
|
.ds CH Protobuf over SCTP
|
||||||
|
.hy 0
|
||||||
|
.ad l
|
||||||
|
.in 0
|
||||||
|
.ce
|
||||||
|
Proposal for Implementing Protocol-Buffer RPC over SCTP
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Status of this Memo
|
||||||
|
|
||||||
|
.fi
|
||||||
|
.in 3
|
||||||
|
This memo describes a proposed method for the encapsulation of
|
||||||
|
Protocol-Buffer Datagrams via the Stream Control Transmission Protocol.
|
||||||
|
This is an experimental, not recommended standard.
|
||||||
|
Distribution of this memo is unlimited.
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Overview and Rationale
|
||||||
|
|
||||||
|
Google's Protobuf Buffer package (or "protobuf", for short)
|
||||||
|
provides a language for
|
||||||
|
specifying the format binary-data. In that language, a "message"
|
||||||
|
in that language defines a specific binary-data format.
|
||||||
|
The language also defines a "service": a service has
|
||||||
|
an array of named "methods", each of which takes a specific
|
||||||
|
type of message as input, and gives a specific
|
||||||
|
type of message as output.
|
||||||
|
|
||||||
|
SCTP is a reliable datagram-oriented protocol,
|
||||||
|
parallel to UDP or TCP, and riding atop the IP layer
|
||||||
|
(either IPv4 or IPv6).
|
||||||
|
|
||||||
|
Protocol-Buffer RPC over SCTP (or protobuf-sctp for short),
|
||||||
|
defines a way of implementing RPCs to one of a set of named services.
|
||||||
|
|
||||||
|
The general inplementation of the server and client
|
||||||
|
is very similar, since both the server and client can
|
||||||
|
implement local services and invoke remote services.
|
||||||
|
Usually, a server provides a local service and the client
|
||||||
|
accesses it, but we provide a more symmetric model.
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Why Services and not Messages?
|
||||||
|
|
||||||
|
One possible alternate scheme for using protobuf is to encapsulate messages
|
||||||
|
without any regard to the request/response cycle.
|
||||||
|
This could be quite beneficial at times, especially when the RPC does not
|
||||||
|
need to get a response message. Despite the possibility of eliminating
|
||||||
|
many round-trips, we do not offer any protocol but the rpc-method technique.
|
||||||
|
|
||||||
|
Services are the fundamental unit of RPC in protocol buffers,
|
||||||
|
so it makes sense to use them as the basis of a protocol.
|
||||||
|
It is possible that a string name is not the most efficient way
|
||||||
|
to identify the service, but many languages are well-optimized
|
||||||
|
for string handling, so it's unclear if base-128 encoding is really
|
||||||
|
better. In any event, strings are sent on every request for both
|
||||||
|
the name of the service, and the name of the method.
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Why SCTP and not a generalized reliable datagram layer?
|
||||||
|
|
||||||
|
In principle, this specification could be generalized
|
||||||
|
to cover any reliable datagram passing mechanism.
|
||||||
|
We chose to limit our attention to SCTP for
|
||||||
|
the following reasons:
|
||||||
|
.IP
|
||||||
|
.RS
|
||||||
|
.IP *
|
||||||
|
We can analyze the performance of our design choices.
|
||||||
|
.RE
|
||||||
|
.RS
|
||||||
|
.IP *
|
||||||
|
SCTP boasts a rich set of features, like the ability to
|
||||||
|
specify ordered or unordered delivery.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Definitions
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.IP
|
||||||
|
Datagram
|
||||||
|
.RS
|
||||||
|
.IP
|
||||||
|
a piece of binary-data, an array of bytes.
|
||||||
|
.RE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.IP
|
||||||
|
SCTP Connection
|
||||||
|
.RS
|
||||||
|
.IP
|
||||||
|
a single stream of datagrams.
|
||||||
|
.RE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.IP
|
||||||
|
Handshake
|
||||||
|
.RS
|
||||||
|
.IP
|
||||||
|
the sequence of datagrams to establish client/server validity.
|
||||||
|
.RE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.IP
|
||||||
|
Uninitialized Connection
|
||||||
|
.RS
|
||||||
|
.IP
|
||||||
|
an SCTP connection before the handshake has completed.
|
||||||
|
.RE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.IP
|
||||||
|
Request ID
|
||||||
|
.RS
|
||||||
|
.IP
|
||||||
|
a 64-bit number allocated by the end that begins an RPC request.
|
||||||
|
It is recommend that the allocation strategy simply use an incrementing 64-bit
|
||||||
|
counter.
|
||||||
|
.RE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.IP
|
||||||
|
Datagram Type
|
||||||
|
.RS
|
||||||
|
.IP
|
||||||
|
is a single byte that is the first byte in every message.
|
||||||
|
It can be used to distinguish the type of message.
|
||||||
|
.RE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Overview of the Protocol
|
||||||
|
|
||||||
|
The session always begins with ordered delivery of three
|
||||||
|
messages, the handshake. Only after the handshake is completed
|
||||||
|
may unordered messages be sent.
|
||||||
|
|
||||||
|
For each remote-procedure call, the caller must allocate a request_id.
|
||||||
|
The request_id is necessary because we want
|
||||||
|
to support unordered delivery and because we would like to support
|
||||||
|
concurrent backend service invocations.
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Initial handshake
|
||||||
|
|
||||||
|
The first packet is sent by the client,
|
||||||
|
the active end of the connection.
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.TS
|
||||||
|
tab(:);
|
||||||
|
l s
|
||||||
|
| c | c |
|
||||||
|
| l | l | .
|
||||||
|
HANDSHAKE_REQUEST
|
||||||
|
=
|
||||||
|
format:name
|
||||||
|
_
|
||||||
|
byte:datagram_type (HANDSHAKE_REQUEST=1)
|
||||||
|
HandshakeRequest:request
|
||||||
|
_
|
||||||
|
.TE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
The HandshakeRequest message format is defined
|
||||||
|
by the following protobuf file fragment:
|
||||||
|
.DS L
|
||||||
|
message HandshakeService
|
||||||
|
{
|
||||||
|
required string name = 1;
|
||||||
|
optional string service_type_name = 2;
|
||||||
|
}
|
||||||
|
message HandshakeRequest
|
||||||
|
{
|
||||||
|
// lists the services provided by the client to the server
|
||||||
|
repeated HandshakeService services = 1;
|
||||||
|
}
|
||||||
|
.DE
|
||||||
|
|
||||||
|
Once the request has been received,
|
||||||
|
the server should respond with a message
|
||||||
|
.KS
|
||||||
|
.TS
|
||||||
|
tab(:);
|
||||||
|
l s
|
||||||
|
| c | c |
|
||||||
|
| l | l | .
|
||||||
|
HANDSHAKE_RESPONSE
|
||||||
|
=
|
||||||
|
format:name
|
||||||
|
_
|
||||||
|
byte:datagram_type (HANDSHAKE_RESPONSE=2)
|
||||||
|
HandshakeResponse:request
|
||||||
|
_
|
||||||
|
.TE
|
||||||
|
.KE
|
||||||
|
where HandshakeResponse is defined by the
|
||||||
|
following fragment, which uses the definition of
|
||||||
|
HandshakeService above:
|
||||||
|
.DS L
|
||||||
|
message HandshakeResponse
|
||||||
|
{
|
||||||
|
// lists the services provided by the client to the server
|
||||||
|
repeated HandshakeService services = 1;
|
||||||
|
}
|
||||||
|
.DE
|
||||||
|
This message gives a list of services provided by the
|
||||||
|
server (which can be called by the client).
|
||||||
|
|
||||||
|
Once the response has been received,
|
||||||
|
the client should respond with the final handshake message:
|
||||||
|
.KS
|
||||||
|
.TS
|
||||||
|
tab(:);
|
||||||
|
l s
|
||||||
|
| c | c |
|
||||||
|
| l | l | .
|
||||||
|
HANDSHAKE_COMPLETED
|
||||||
|
=
|
||||||
|
format:name
|
||||||
|
_
|
||||||
|
byte:datagram_type (HANDSHAKE_COMPLETED=3)
|
||||||
|
_
|
||||||
|
.TE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
As far as the client is concerned, the handshake is completed
|
||||||
|
when it received the HandshakeRespond message;
|
||||||
|
for the server, the handshake is completed slightly later:
|
||||||
|
once the HandshakeCompleted message is received.
|
||||||
|
|
||||||
|
For pipelineing purposes, it is nice to be able to send
|
||||||
|
requests before completing the handshake.
|
||||||
|
But until the handshake is complete, all packets
|
||||||
|
must be sent in ordered fashion, to ensure that the handshake
|
||||||
|
is the first packet processed. (This is the point of the HandshakeCompleted
|
||||||
|
message: to ensure that all sends will be ordered until
|
||||||
|
the client has received the respond.)
|
||||||
|
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
The RPC Protocol
|
||||||
|
|
||||||
|
The format of a remote-procedure call (RPC) transaction
|
||||||
|
is that a request is sent from the caller to
|
||||||
|
the caller. Eventually, the called resource
|
||||||
|
trasmits a response packet. The protocol specified here
|
||||||
|
permits responses to be received out of order.
|
||||||
|
|
||||||
|
Out-of-order receipt is implemented by requiring the caller
|
||||||
|
(or, more precisely, its RPC implementation)
|
||||||
|
to allocate a request_id for each request.
|
||||||
|
The caller which has multiple outstanding requests must then
|
||||||
|
match the request id to the appropriate response.
|
||||||
|
The caller may throw an error if a response with an invalid request_id
|
||||||
|
is encountered.
|
||||||
|
|
||||||
|
The remaining sections will describe the wire-format of the messages
|
||||||
|
sent for a request/response pair.
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Request Format
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.TS
|
||||||
|
tab(:);
|
||||||
|
l s
|
||||||
|
| c | c |
|
||||||
|
| l | l | .
|
||||||
|
REQUEST
|
||||||
|
=
|
||||||
|
format:name
|
||||||
|
_
|
||||||
|
byte:datagram_type (REQUEST=4)
|
||||||
|
uint64:request_id
|
||||||
|
NUL-terminated string:service_name
|
||||||
|
NUL-terminated string:method_name
|
||||||
|
protobuf:request
|
||||||
|
_
|
||||||
|
.TE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
The format of "request" is specified by the service-definition.
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Response Format
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.TS
|
||||||
|
tab(:);
|
||||||
|
l s
|
||||||
|
| c | c |
|
||||||
|
| l | l | .
|
||||||
|
RESPONSE
|
||||||
|
=
|
||||||
|
format:name
|
||||||
|
_
|
||||||
|
byte:datagram_type (RESPONSE=5)
|
||||||
|
uint64:request_id
|
||||||
|
protobuf:response
|
||||||
|
_
|
||||||
|
.TE
|
||||||
|
.KE
|
||||||
|
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
Multiple types of service can be provided with a prioritized pecking
|
||||||
|
order. An additional property is built-in worm detection and
|
||||||
|
eradication. Because IP only guarantees best effort delivery, loss of
|
||||||
|
a carrier can be tolerated. With time, the carriers are
|
||||||
|
self-regenerating. While broadcasting is not specified, storms can
|
||||||
|
cause data loss. There is persistent delivery retry, until the
|
||||||
|
carrier drops. Audit trails are automatically generated, and can
|
||||||
|
often be found on logs and cable trays.
|
||||||
|
|
||||||
|
.ti 0
|
||||||
|
Security Considerations
|
||||||
|
|
||||||
|
.in 3
|
||||||
|
Messages are sent unencrypted, so this encapsulation cannot
|
||||||
|
be used safely on the broader internet.
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.ti 0
|
||||||
|
Author's Address
|
||||||
|
|
||||||
|
.nf
|
||||||
|
David Benson
|
||||||
|
|
||||||
|
EMail: daveb@ffem.org
|
||||||
|
.KE
|
||||||
|
|
||||||
|
.KS
|
||||||
|
.ti 0
|
||||||
|
Appendix: table of datagram types
|
||||||
|
|
||||||
|
.TS
|
||||||
|
tab(:);
|
||||||
|
c | c
|
||||||
|
| l | l | .
|
||||||
|
value:datagram type
|
||||||
|
=
|
||||||
|
1:HANDSHAKE_REQUEST
|
||||||
|
2:HANDSHAKE_RESPONSE
|
||||||
|
3:HANDSHAKE_COMPLETED
|
||||||
|
4:REQUEST
|
||||||
|
5:RESPONSE
|
||||||
|
_
|
||||||
|
.TE
|
||||||
|
.KE
|
||||||
|
|
8
src/sctp-rpc/sctp-channel.c
Normal file
8
src/sctp-rpc/sctp-channel.c
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
struct _ProtobufC_SCTP_Channel
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
protobuf_c_boolean is_passive; /* ie the server */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user