Gwyfile Library
client.c

Example of sending and receiving GWY objects over a network stream. This is the client part which sends a query and receives and an answer.

/*
* $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: client.c 396 2023-02-08 14:03:53Z yeti-dn $
*
* This example is the client for server.c. It shows how send a query 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"
const char *servername = "localhost";
static void ask_the_server(GwyfileObject *message, int sock, unsigned int nrepeat);
int
main(int argc, char *argv[])
{
struct sockaddr_in serv_addr;
struct hostent *server;
unsigned int i, array_len = 100000;
double *array;
int portno, sock;
if (argc < 2) {
printf("Usage: server PORTNUMBER\n");
exit(EXIT_SUCCESS);
}
portno = atoi(argv[1]);
/* Standard network setup. */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Cannot create socket");
exit(EXIT_FAILURE);
}
server = gethostbyname(servername);
if (server == NULL) {
fprintf(stderr, "Cannot resolve hostname %s.\n", servername);
exit(EXIT_FAILURE);
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
fprintf(stderr, "Cannot connect to address %s, port %d: %s\n", servername, portno, strerror(errno));
exit(EXIT_FAILURE);
}
array = (double*)malloc(array_len*sizeof(double));
for (i = 0; i < array_len; i++)
array[i] = i;
/* Send queries in bursts (3 and 5). The server has to handle correctly what is essentially a continuous stream
* of serialised objects. This is kind of stressing for it because we do not read anything back until we send
* the entire burst so the server has to buffer it all. If we send too much the server can decide enough is enough
* and kick us out. */
ask_the_server(gwyfile_object_new("MSG",
gwyfile_item_new_char("first letter", 'A'),
gwyfile_item_new_bool("ok", false),
gwyfile_item_new_double("value", 3.5),
NULL),
sock, 3);
ask_the_server(gwyfile_object_new("MSG",
gwyfile_item_new_char("second letter", 'B'),
gwyfile_item_new_string_copy("text", "Shark"),
gwyfile_item_new_int64("sum", 1234567890L),
gwyfile_item_new_double_array_const("array", array, array_len),
NULL),
sock, 5);
free(array);
return 0;
}
static void
ask_the_server(GwyfileObject *message, int sock, unsigned int nrepeat)
{
unsigned int nitems, i, ir;
GwyfileObject *answer;
GwyfileItem *item;
char *buffer = NULL;
size_t bufsize = 0, have_bytes = 0;
int err;
/* Send all queries over the network without checking for responses. This works fine for short messages. Be more
* careful when sending large data. */
for (ir = 0; ir < nrepeat; ir++) {
if (send_gwyfile_message(message, sock, &buffer, &bufsize) < 0)
exit(EXIT_FAILURE);
}
printf("Sent request (×%u), waiting for response.\n\n", nrepeat);
/* Expect as many answeres as we sent out queries. */
for (ir = 0; ir < nrepeat; ir++) {
printf("Getting answer #%u.\n", ir);
/* Make recv_gwyfile_message2() blocking. */
while (!(answer = recv_gwyfile_message2(sock, &buffer, &bufsize, &have_bytes, &err)) && err == EAGAIN)
;
if (err < 0)
exit(EXIT_FAILURE);
assert(answer);
nitems = gwyfile_object_nitems(answer);
printf("Answer has %u items\n", nitems);
for (i = 0; i < nitems; i++) {
item = gwyfile_object_get_nth(answer, i);
printf("Item[%u] is called \"%s\" (type %c).\n", i, gwyfile_item_name(item), gwyfile_item_type(item));
}
printf("\n");
}
free(buffer);
}
/* 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 : */
GwyfileItem * gwyfile_item_new_bool(const char *name, bool value)
Creates a new boolean GWY file item.
Definition: gwyfile.c:5406
GwyfileItem * gwyfile_item_new_char(const char *name, char value)
Creates a new character GWY file item.
Definition: gwyfile.c:5456
GwyfileItem * gwyfile_item_new_double_array_const(const char *name, const double *value, uint32_t array_length)
Creates a new double array GWY file item.
Definition: gwyfile.c:6760
GwyfileItem * gwyfile_item_new_double(const char *name, double value)
Creates a new double GWY file item.
Definition: gwyfile.c:5606
GwyfileItem * gwyfile_item_new_int64(const char *name, int64_t value)
Creates a new 64bit integer GWY file item.
Definition: gwyfile.c:5556
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_name(const GwyfileItem *item)
Obtains the name of a GWY file data item.
Definition: gwyfile.c:7311
GwyfileItemType gwyfile_item_type(const GwyfileItem *item)
Obtains the type of a GWY file data item.
Definition: gwyfile.c:7298
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
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