2

I'm working a sort of "restaurant" implementation in C with client-server. I am trying to send the following structure through a FIFO:

   typedef struct { 
   int numtable;            //table number to send answer
   char timestamp[20];      //simple timestamp
   int order[MENUSZ];       //array of int with dish IDs
   } request;

About this struct, I basically send to the server the table number, to "build" the client FIFO name through a template, a timestamp, and order is a simple array filled with randomly chosen integers to "create" a sort of random menu request. With this setup I didn't have problems, using

   write(server_fd, &request, sizeof(request))

I had problems when I wanted to transform the array order[MENUSZ] in a pointer, to make a dynamic array, like this:

    typedef struct {    
   int numtable;            
   char timestamp[20];      
   int *order;      
   } request;

After changing the struct, I used the malloc function to allocate enough space for the array:

   request->order = malloc(sizeof(int)*numclients+1);

The array is fullfilled correctly, but for some reason the server can't read from the FIFO after I added this pointer, by doing

   read(server_fd, &request, sizeof(request));

I can't figure out why it doesn't work with this pointer. Am I doing something wrong?

edornd
  • 441
  • 1
  • 4
  • 18
  • 1
    You can not transfer a pointer through a FIFO to another process. – Andreas Fester Oct 07 '14 at 11:57
  • you will transfer a (meaningless) pointer (4 bytes). If using the revised structure, then the code needs to send each field, including the fields in the malloc'd area. Much more messy and error prone. – user3629249 Oct 08 '14 at 04:17

2 Answers2

2

The array is fullfilled correctly, but for some reason the server can't read from the FIFO after I added this pointer, by doing

read(server_fd, &request, sizeof(request));

You are transferring your structure, which includes a pointer, and the value of the pointer will be transferred correctly, but it will not point to a valid address in the destination process, neither will there be memory allocated where the pointer points to.

Hence, you need to transfer the array separately and recreate the pointer in the destination process, something like:

read(server_fd, &request, sizeof(request));

/* allocate memory for request->order in the reader process */
request->order = malloc(sizeof(int)*numclients+1);
read(server_fd, request->order, sizeof(int)*numclients+1);

A yet better solution would be to also transfer the size of the array inside your structure.

On the sending side, you then need to send both, the structure and the array contents, something like

write(server_fd, &request, sizeof(request))
write(server_fd, request->order, sizeof(int)*numclients+1));
Community
  • 1
  • 1
Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
  • 1
    Perhaps outside the scope of this question, but the flexible array at the end of a struct introduced in C99, together with the element count inside the struct itself, would be something worth considering. – Arkku Oct 07 '14 at 12:16
  • I would also replace all occurrences of `sizeof(int)` with `sizeof(request->order[0])`. – Arkku Oct 07 '14 at 12:18
  • @Arkku Yes, definitely reasonable suggestions - I just wanted to focus on the actual issue ;) Flexible array should work, and would even allow to send the whole structure in a single step, but it would not work when there are multiple pointers in the structure – Andreas Fester Oct 07 '14 at 12:21
  • Only one flexible array is permitted per struct since it must be in the end. Non-flexible arrays would not interfere. (Pointers to arrays would, but then that was the thing we were trying to avoid. =) – Arkku Oct 07 '14 at 12:22
  • 1
    But, anyhow, my point was just to make the OP aware of these possibilities, not to complicate the answer. =) – Arkku Oct 07 '14 at 12:24
0

It is because sizeof(request) no longer tells you the size of the combined structure. Try this

typedef struct {    
   int numtable;            
   char timestamp[20];      
   int order[1];      
} request;

When you have a new request

int reqsize = sizeof(request) + sizeof(int) * numclients;
request* req = malloc(reqsize);

This allows you to use req->order[1] to req->order[numclients - 1]. When you send it, use

 write(server_fd, reqsize, sizeof(int))
 write(server_fd, req, reqsize)

When reading

read(server_fd, &reqsize, sizeof(int))

Then allocate the request before reading

request* req = malloc(reqsize)
read(server_fd, req, reqsize)

This technique uses "the chumminess of C" http://c-faq.com/struct/structhack.html, which, as far as I know, works on all implementations of C.

cup
  • 7,589
  • 4
  • 19
  • 42
  • I am not sure whether to be impressed or horrified by this technique. Probably both. – Degustaf Oct 07 '14 at 14:16
  • Just found out that you C99 supports the concept of flexible arrays - as long as it is the last item in the list. So you can declare int order[] for c99 and it should work. Flex arrays also work on VS2008. – cup Oct 07 '14 at 17:51