Gwyfile Library
server.c

Example of sending and receiving GWY objects over a network stream. This is the server part which listens to requests and answers them.

/*
* $Id: clientserver.h 351 2021-08-30 13:55:14Z yeti-dn $
*
* This file provides some common functions for client.c and server.c.
*
* I, the copyright holder of this work, release this work into the public
* domain. This applies worldwide. In some countries this may not be legally
* possible; if so: I grant anyone the right to use this work for any purpose,
* without any conditions, unless such conditions are required by law.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include "gwyfile.h"
static void
ensure_buffer_size(char **buffer, size_t *bufsize, size_t len)
{
if (len <= *bufsize)
return;
if (2*(*bufsize) >= len)
*bufsize *= 2;
else
*bufsize = len;
*buffer = realloc(*buffer, *bufsize);
}
#ifdef __GNUC__
__attribute__((unused))
#endif
static GwyfileObject*
recv_gwyfile_message(int sock, char **buffer, size_t *bufsize, int *err)
{
GwyfileError *error = NULL;
GwyfileObject *message;
size_t objsize, len = 0;
ssize_t read_size;
/* Purposedly make the buffer small at the beginning. */
if (!*bufsize || !*buffer)
ensure_buffer_size(buffer, bufsize, 16);
*err = -1;
printf("Trying to read %u bytes.\n", (unsigned)(*bufsize - len));
while ((read_size = read(sock, *buffer + len, *bufsize - len)) > 0) {
printf("Read %ld bytes from socket %d\n", (long int)read_size, sock);
len += read_size;
/* Process the message we have in the buffer. */
if (!(message = gwyfile_object_read_memory(*buffer, len, &objsize, &error))) {
assert(error);
/* There is an incomplete message. */
if (len == *bufsize) {
ensure_buffer_size(buffer, bufsize, 2*(*bufsize));
fprintf(stderr, "Enlarging buffer to %lu.\n", (unsigned long)(*bufsize));
}
continue;
}
else {
fprintf(stderr, "Failed to read message. %s\n", error->message);
fprintf(stderr, "Giving up.\n");
return NULL;
}
}
printf("Obtained message of %lu bytes\n", (unsigned long int)objsize);
*err = 0;
return message;
}
/* If either read_size < 0 or we have still something in buffer then an error occurred. */
if (read_size < 0) {
fprintf(stderr, "recv() failed: %s.\n", strerror(errno));
return NULL;
}
else if (len > 0) {
fprintf(stderr, "Incomplete message.\n");
return NULL;
}
/* Returning NULL with zero error means there is simply nothing more to read. */
*err = 0;
return NULL;
}
#ifdef __GNUC__
__attribute__((unused))
#endif
static GwyfileObject*
recv_gwyfile_message2(int sock, char **buffer, size_t *bufsize, size_t *have_bytes, int *err)
{
GwyfileError *error = NULL;
GwyfileObject *message;
size_t objsize;
ssize_t read_size;
int first_try = 1;
/* Purposedly make the buffer small at the beginning. */
if (!*bufsize || !*buffer)
ensure_buffer_size(buffer, bufsize, 16);
while (1) {
if (!first_try || !*have_bytes) {
printf("Trying to read %u bytes.\n", (unsigned)(*bufsize - *have_bytes));
if ((read_size = read(sock, *buffer + *have_bytes, *bufsize - *have_bytes)) <= 0)
break;
printf("Read %ld bytes from socket %d\n", (long int)read_size, sock);
*have_bytes += read_size;
}
else {
printf("Trying to use the %lu bytes left in buffer.\n", (unsigned long int)(*have_bytes));
}
/* Process the message we have in the buffer. */
if ((message = gwyfile_object_read_memory(*buffer, *have_bytes, &objsize, &error))) {
printf("Obtained message of %lu bytes (%lu bytes remaining)\n",
(unsigned long int)objsize, (unsigned long int)(*have_bytes - objsize));
/* Handled piece-wise objects by keeping whatever extra data we received in the buffer. */
memmove(*buffer, *buffer + objsize, *have_bytes - objsize);
*have_bytes -= objsize;
*err = 0;
return message;
}
assert(error);
goto bad_message;
/* There is an incomplete message. If it is incomplete because we would block, just pass it up.
* Otherwise enlarge the buffer. */
if (*have_bytes == *bufsize) {
ensure_buffer_size(buffer, bufsize, 2*(*bufsize));
fprintf(stderr, "Enlarging buffer to %lu.\n", (unsigned long)(*bufsize));
}
first_try = 0;
}
/* If either read_size < 0 or we have still something in buffer then an error occurred. */
*err = errno;
if (read_size < 0) {
fprintf(stderr, "recv() failed: %s.\n", strerror(errno));
return NULL;
}
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
*err = EAGAIN;
fprintf(stderr, "Incomplete message so far.\n");
return NULL;
}
/* Returning NULL with zero error means there is simply nothing more to read. */
if (errno)
fprintf(stderr, "We got zero from recv() with unexpected error %s.\n", strerror(errno));
return NULL;
bad_message:
fprintf(stderr, "Failed to read message. %s\n", error->message);
fprintf(stderr, "Giving up.\n");
*err = EINVAL;
return NULL;
}
#ifdef __GNUC__
__attribute__((unused))
#endif
static int
send_gwyfile_message(GwyfileObject *message, int sock, char **buffer, size_t *bufsize)
{
ssize_t pos, write_size;
size_t len;
len = gwyfile_object_size(message);
ensure_buffer_size(buffer, bufsize, len);
len = gwyfile_object_write_memory(message, *buffer, *bufsize, NULL);
assert(len);
pos = 0;
while (len > 0) {
write_size = write(sock, *buffer + pos, len);
if (write_size < 0) {
int saved_errno = errno;
fprintf(stderr, "send() failed: %s.\n", strerror(errno));
errno = saved_errno;
return -1;
}
len -= write_size;
pos += write_size;
}
return 0;
}
/* vim: set cin et ts=4 sw=4 columns=120 tw=119 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
void gwyfile_error_clear(GwyfileError **error)
Clears a GwyfileError.
Definition: gwyfile.c:8990
@ GWYFILE_ERROR_TRUNCATED
The top-level object is truncated.
Definition: gwyfile.h:128
@ GWYFILE_ERROR_DOMAIN_DATA
Libgwyfle data error domain: codes are equal to GwyfileErrorCode values. They represent data format e...
Definition: gwyfile.h:112
size_t gwyfile_object_size(const GwyfileObject *object)
Obtains the serialized size of a GWY file data object.
Definition: gwyfile.c:4615
GwyfileObject * gwyfile_object_read_memory(const void *buffer, size_t size, size_t *bytes_read, GwyfileError **error)
Reads a GWY file data object from a memory buffer.
Definition: gwyfile.c:5196
size_t gwyfile_object_write_memory(const GwyfileObject *object, void *buffer, size_t size, GwyfileError **error)
Writes a GWY file data object to a memory buffer.
Definition: gwyfile.c:5255
Gwyfile Library.
Detailed information about an error.
Definition: gwyfile.h:131
char * message
Text message describing the error. It is allocated when the error struct is created and freed with gw...
Definition: gwyfile.h:134
int code
Particular error code. For errors from the system domain it is equal to errno while for libgwyfile-sp...
Definition: gwyfile.h:133
GwyfileErrorDomain domain
Identifier of the class of errors.
Definition: gwyfile.h:132
/*
* $Id: server.c 396 2023-02-08 14:03:53Z yeti-dn $
*
* This example is the server for client.c. It shows how to answer queries using libgwyfile as a serialisation
* framework.
*
* I, the copyright holder of this work, release this work into the public domain. This applies worldwide. In some
* countries this may not be legally possible; if so: I grant anyone the right to use this work for any purpose,
* without any conditions, unless such conditions are required by law.
*/
#include "clientserver.h"
static void connection_handler(int sock);
static GwyfileObject* answer_to_message (GwyfileObject *message);
int
main(int argc, char *argv[])
{
int portno, socket_desc, client_sock, c = sizeof(struct sockaddr_in);
struct sockaddr_in server, client;
if (argc < 2) {
printf("Usage: server PORTNUMBER\n");
exit(EXIT_SUCCESS);
}
/* Standard network setup. */
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
perror("Cannot create socket");
exit(EXIT_FAILURE);
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
portno = atoi(argv[1]);
server.sin_port = htons(portno);
if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("bind() failed");
exit(EXIT_FAILURE);
}
listen(socket_desc, 3);
/* Main loop handling client requests. It runs indefinitely, i.e. until the process is terminated externally. */
while ((client_sock = accept(socket_desc, (struct sockaddr*)&client, (socklen_t*)&c)) > 0) {
printf("Connection accepted.\n");
/* Normally we would do this in a subprocess or thread. Do not bother in the example. */
connection_handler(client_sock);
close(client_sock);
}
if (client_sock < 0) {
perror("accept() failed");
exit(EXIT_FAILURE);
}
return 0;
}
/* Handle a client connection. */
static void
connection_handler(int sock)
{
GwyfileObject *message, *answer;
char *recvbuffer = NULL, *sendbuffer = NULL;
size_t recvbufsize = 0, sendbufsize = 0, recvlen = 0;
int err;
/* Receive data from the client. Use a simple busy wait to block on recv_gwyfile_message2(). */
while ((message = recv_gwyfile_message2(sock, &recvbuffer, &recvbufsize, &recvlen, &err)) || err == EAGAIN) {
if (err == EAGAIN)
continue;
answer = answer_to_message(message);
/* Random sleep for the spectators. Otherwise everything happens in an instant. */
usleep(rand() % 1000000);
if ((err = send_gwyfile_message(answer, sock, &sendbuffer, &sendbufsize)) < 0) {
break;
}
}
if (err == 0)
printf("Communication finished successfully.\n\n");
else
fprintf(stderr, "Communication failed.\n\n");
free(sendbuffer);
free(recvbuffer);
}
static GwyfileObject*
answer_to_message(GwyfileObject *message)
{
const char *dont = "I thought I had told you a thousand times that I really do not like ";
GwyfileObject *answer;
GwyfileItem *item, *newitem;
unsigned int nitems, i, len, ll = strlen(dont);
const char *name, *s;
char *t;
/* Send something back depending on what we got. Mostly various modification of the items in the query. */
answer = gwyfile_object_new("MSG", NULL);
nitems = gwyfile_object_nitems(message);
for (i = 0; i < nitems; i++) {
item = gwyfile_object_get_nth(message, i);
type = gwyfile_item_type(item);
name = gwyfile_item_name(item);
newitem = NULL;
if (type == GWYFILE_ITEM_INT32)
else if (type == GWYFILE_ITEM_DOUBLE)
else if (type == GWYFILE_ITEM_BOOL)
else if (type == GWYFILE_ITEM_INT64) {
char hexbuf[8*2+3];
snprintf(hexbuf, sizeof(hexbuf), "0x%016lx", (unsigned long)gwyfile_item_get_int64(item));
newitem = gwyfile_item_new_string_copy(name, hexbuf);
}
else if (type == GWYFILE_ITEM_STRING) {
len = strlen(s);
t = malloc(len + ll + 1);
memcpy(t, dont, ll);
memcpy(t + ll, s, len+1);
newitem = gwyfile_item_new_string(name, t);
}
else if (type == GWYFILE_ITEM_DOUBLE_ARRAY) {
/* Since @message can be freed before we send back the response, take the ownership of the data array. */
double *data = gwyfile_item_take_double_array(item);
unsigned int j, n = gwyfile_item_array_length(item);
for (j = 0; j < n; j++)
data[i] *= 2.0;
/* This is slightly reckless as we completely transfer ownership and data[] is allowed to be freed
* immediately in gwyfile_item_new_double_array(). Nothing in Gwyfile is going to actually accesss (or
* free) the data in @item, but the possibly dangling pointer will be lying around. */
newitem = gwyfile_item_new_double_array(name, data, n);
}
else {
fprintf(stderr, "Cannot take ownership of double array item %s.\n", name);
}
}
if (newitem)
gwyfile_object_add(answer, newitem);
}
return answer;
}
/* vim: set cin et ts=4 sw=4 columns=120 tw=119 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
bool gwyfile_item_get_bool(const GwyfileItem *item)
Gets the boolean value contained in a GWY file data item.
Definition: gwyfile.c:5440
GwyfileItem * gwyfile_item_new_bool(const char *name, bool value)
Creates a new boolean GWY file item.
Definition: gwyfile.c:5406
double * gwyfile_item_take_double_array(GwyfileItem *item)
Takes the double array value contained in a GWY file data item.
Definition: gwyfile.c:6844
GwyfileItem * gwyfile_item_new_double_array(const char *name, double *value, uint32_t array_length)
Creates a new double array GWY file item.
Definition: gwyfile.c:6638
GwyfileItem * gwyfile_item_new_double(const char *name, double value)
Creates a new double GWY file item.
Definition: gwyfile.c:5606
double gwyfile_item_get_double(const GwyfileItem *item)
Gets the double value contained in a GWY file data item.
Definition: gwyfile.c:5640
int32_t gwyfile_item_get_int32(const GwyfileItem *item)
Gets the 32bit integer value contained in a GWY file data item.
Definition: gwyfile.c:5540
GwyfileItem * gwyfile_item_new_int32(const char *name, int32_t value)
Creates a new 32bit integer GWY file item.
Definition: gwyfile.c:5506
int64_t gwyfile_item_get_int64(const GwyfileItem *item)
Gets the 64bit integer value contained in a GWY file data item.
Definition: gwyfile.c:5590
GwyfileItem * gwyfile_item_new_string(const char *name, char *value)
Creates a new string GWY file item.
Definition: gwyfile.c:5660
GwyfileItem * gwyfile_item_new_string_copy(const char *name, const char *value)
Creates a new string GWY file item.
Definition: gwyfile.c:5712
const char * gwyfile_item_get_string(const GwyfileItem *item)
Gets the string value contained in a GWY file data item.
Definition: gwyfile.c:5814
uint32_t gwyfile_item_array_length(const GwyfileItem *item)
Obtains the array length of a GWY file data item.
Definition: gwyfile.c:7326
const char * gwyfile_item_name(const GwyfileItem *item)
Obtains the name of a GWY file data item.
Definition: gwyfile.c:7311
GwyfileItemType
Definition: gwyfile.h:87
bool gwyfile_item_owns_data(const GwyfileItem *item)
Reports if a GWY file item owns its data.
Definition: gwyfile.c:8082
GwyfileItemType gwyfile_item_type(const GwyfileItem *item)
Obtains the type of a GWY file data item.
Definition: gwyfile.c:7298
@ GWYFILE_ITEM_INT32
32bit integer.
Definition: gwyfile.h:90
@ GWYFILE_ITEM_DOUBLE_ARRAY
Array of IEEE double precision floating point numbers.
Definition: gwyfile.h:98
@ GWYFILE_ITEM_STRING
String of characters.
Definition: gwyfile.h:93
@ GWYFILE_ITEM_BOOL
Boolean (true or false).
Definition: gwyfile.h:88
@ GWYFILE_ITEM_INT64
64bit integer.
Definition: gwyfile.h:91
@ GWYFILE_ITEM_DOUBLE
IEEE double precision floating point number.
Definition: gwyfile.h:92
GwyfileObject * gwyfile_object_new(const char *name,...)
Creates a new GWY file object.
Definition: gwyfile.c:607
GwyfileItem * gwyfile_object_get_nth(const GwyfileObject *object, unsigned int n)
Gets n-th data item from a GWY file object.
Definition: gwyfile.c:4875
bool gwyfile_object_add(GwyfileObject *object, GwyfileItem *item)
Adds an data item to a GWY file data object.
Definition: gwyfile.c:4652
unsigned int gwyfile_object_nitems(const GwyfileObject *object)
Obtains the number of items in a GWY file data object.
Definition: gwyfile.c:4920
void gwyfile_object_free(GwyfileObject *object)
Frees a GWY file data object.
Definition: gwyfile.c:4577