diff options
Diffstat (limited to 'example/server.c')
-rw-r--r-- | example/server.c | 149 |
1 files changed, 149 insertions, 0 deletions
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); + } +} |