diff options
Diffstat (limited to 'example')
-rw-r--r-- | example/Makefile | 11 | ||||
-rw-r--r-- | example/fileproto.proto | 26 | ||||
-rw-r--r-- | example/server.c | 149 |
3 files changed, 186 insertions, 0 deletions
diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 00000000..0f08f2d7 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,11 @@ +CFLAGS=-ansi -Wall -Werror -I .. -g -O0 +DEPS=../pb_decode.c ../pb_decode.h ../pb_encode.c ../pb_encode.h ../pb.h + +all: server + +%: %.c $(DEPS) fileproto.h + $(CC) $(CFLAGS) -o $@ $< ../pb_decode.c ../pb_encode.c fileproto.c + +fileproto.h: fileproto.proto ../generator/nanopb_generator.py + protoc -I. -I../generator -I/usr/include -ofileproto.pb $< + python ../generator/nanopb_generator.py fileproto.pb diff --git a/example/fileproto.proto b/example/fileproto.proto new file mode 100644 index 00000000..e2786b15 --- /dev/null +++ b/example/fileproto.proto @@ -0,0 +1,26 @@ +import "nanopb.proto"; + +// This defines protocol for a simple server that lists files. +// +// If you come from high-level programming background, the hardcoded +// maximum lengths may disgust you. However, if your microcontroller only +// has a few kB of ram to begin with, setting reasonable limits for +// filenames is ok. +// +// On the other hand, using the callback interface, it is not necessary +// to set a limit on the number of files in the response. + +message ListFilesRequest { + optional string path = 1 [default = "/", (nanopb).max_size = 128]; +} + +message FileInfo { + required uint64 inode = 1; + required string name = 2 [(nanopb).max_size = 128]; +} + +message ListFilesResponse { + optional bool path_error = 1 [default = false]; + repeated FileInfo file = 2; +} + diff --git a/example/server.c b/example/server.c new file mode 100644 index 00000000..a671f4cb --- /dev/null +++ b/example/server.c @@ -0,0 +1,149 @@ +/* This is a simple TCP server that listens on port 1234 and provides lists + * of files to clients, using a protocol defined in file_server.proto. + * + * It directly deserializes and serializes messages from network, minimizing + * memory use. + * + * For flexibility, this example is implemented using posix api. + * In a real embedded system you would typically use some other kind of + * a communication and filesystem layer. + */ + +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <unistd.h> +#include <dirent.h> +#include <stdio.h> +#include <string.h> + +#include <pb_encode.h> +#include <pb_decode.h> + +#include "fileproto.h" + +bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) +{ + int fd = *(int*)stream->state; + return send(fd, buf, count, 0) == count; +} + +bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count) +{ + int fd = *(int*)stream->state; + + if (buf == NULL) + { + /* Well, this is a really inefficient way to skip input. */ + /* It is only used when there are unknown fields. */ + char dummy; + while (count-- && recv(fd, &dummy, 1, 0) == 1); + return count == 0; + } + + return recv(fd, buf, count, MSG_WAITALL) == count; +} + +bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, const void *arg) +{ + DIR *dir = (DIR*) arg; + struct dirent *file; + FileInfo fileinfo; + + while ((file = readdir(dir)) != NULL) + { + fileinfo.inode = file->d_ino; + strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name)); + fileinfo.name[sizeof(fileinfo.name) - 1] = '\0'; + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + if (!pb_enc_submessage(stream, field, &fileinfo)) + return false; + } + + return true; +} + +void handle_connection(int connfd) +{ + ListFilesRequest request; + ListFilesResponse response; + pb_istream_t input = {&read_callback, &connfd, SIZE_MAX}; + pb_ostream_t output = {&write_callback, &connfd, SIZE_MAX, 0}; + DIR *directory; + + if (!pb_decode(&input, ListFilesRequest_fields, &request)) + { + printf("Decoding failed.\n"); + return; + } + + directory = opendir(request.path); + + printf("Listing directory: %s\n", request.path); + + if (directory == NULL) + { + perror("opendir"); + + response.has_path_error = true; + response.path_error = true; + response.file.funcs.encode = NULL; + } + else + { + response.has_path_error = false; + response.file.funcs.encode = &listdir_callback; + response.file.arg = directory; + } + + if (!pb_encode(&output, ListFilesResponse_fields, &response)) + { + printf("Encoding failed.\n"); + } +} + +int main(int argc, char **argv) +{ + int listenfd, connfd; + struct sockaddr_in servaddr; + + listenfd = socket(AF_INET, SOCK_STREAM, 0); + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + servaddr.sin_port = htons(1234); + if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) + { + perror("bind"); + return 1; + } + + if (listen(listenfd, 5) != 0) + { + perror("listen"); + return 1; + } + + for(;;) + { + connfd = accept(listenfd, NULL, NULL); + + if (connfd < 0) + { + perror("accept"); + return 1; + } + + printf("Got connection.\n"); + + handle_connection(connfd); + + printf("Closing connection.\n"); + + close(connfd); + } +} |