0

I need to make a bi-directional communication between two processes, I have tried using a classic client-server connection, in which the client sends a stream of bits to the server, but I don't see how can I make the server reply other than destroying the sockets and switching client-server to server-client.

FYI, the whole idea of this project is to program a Remote Filesystem, and a Filesystem Client, using FUSE to interact with the RFS. And in this case, if i should ask FUSE for the attributes of a directory in the Filesystem Client, FUSE should send the directory path to the RFS and the RFS should answer back with this directory attributes.

I would very much appreciate your comments, suggestions and help on this matter.

thank you very much in advance!

char* receive_variable_stream(int client_desc){         //esta es la mia
char* buff
int buff_size=10;
buff = malloc(buff_size);
buff_size = recv(client_desc,buff,buff_size,MSG_PEEK);
buff = realloc(buff,buff_size);
recv(client_desc,buff,buff_size,MSG_WAITALL);
return buff;

}

Guillermo Gruschka
  • 167
  • 1
  • 3
  • 16

1 Answers1

4

TCP is a peer-to-peer protocol - after the server accepts a client connection, data can be sent in either direction (unless one side shuts down the socket for sending or receiving, which is an unusual thing to bother doing even if you have a one-direction application protocol). Each side simply uses write/send and read/recv.

For TCP at least, "client/server" simply describes the active client initiating a connection to the passively listening server. The communication capabilities on the connection are afterwards identical. Clients are often simpler, but a client that makes multiple simultaneous connections may be as complicated to code as a server handling multiple clients. And it's possible for clients to also be servers, and even for a "server" process to connect back to one of its clients (that happens also to be a server).

There are plenty of snippets of sample code for such connections - I usually search for the GNU libc socket example code to let me code up a basic server and client quickly (it illustrates select() based handling of multiple clients; multi-threading is another valid alternative). Have a read through http://www.gnu.org/software/libc/manual/html_node/Connections.html for both background information and that example code.

EDIT: discussion of the code you added to your question...

int buff_size = 10;
char* buff = malloc(buff_size);
buff_size = recv(client_desc,buff,buff_size,MSG_PEEK);
buff = realloc(buff,buff_size);
recv(client_desc,buff,buff_size,MSG_WAITALL); 

What the first recv() call does is either report that the client's disconnected [return value 0] or some other error/exception has occured [-1], or wait (if necessary) until it can peek at (copy into buff without removing from the stream) at least 1 byte and at most 10 bytes from the client [returning the number of bytes actually read].

So, say your client knows they have one "logical" message of N bytes and writes it to their end of the connection. Soon after, they might write another "logical" message of length O bytes. Then your application gets scheduled on the CPU, and your recv() logic kicks in. The peek could retrieve anywhere from 1 to max(10, N + O) bytes... all of which is entirely valid non-eroneous bahaviour. That data is simply the first one/few byte(s) from the two messages sent. The values of N and O can not be reliably inferred from the number of bytes "peeked upon" (i.e. from buff_size = recv(...)).

To make this more tangible, say the first message was "ABCDEFGHIJKLMNOPQRSTUVWXYZ". Your peek could load into buff "A" (in which case buff_size would be set to 1), "AB" (2), through to "ABCDEFGHIJ" (10), but there's no way your program could know how many more bytes were in that message. If your messages were "ABCD" and "EFGH" you might still get anything from "A" through "ABCDEFGH" when peeking.

There are only a few practical ways to let the receiving side know how much data to expect:

  • the programmer writing the recv() code knows the send() code always sends some specific number of bytes
  • the data received embeds an indicator of the amount of data in the message
    • it could have a "header" in which the first bit of data indicates the total message length
    • the problem of knowing how long the header is hierarchically invokes these same choices, but often it's practical to decide that the message header can be fixed length (the first choice above), and the message body can be flexibly sized using one of the other conventions
    • it could use a "delimiter" character that always denotes the end of a message, popular choices include linefeeds and/or carriage returns and NUL characters (for binary streams)

These design decisions are part of creating an application-level protocol for communication between the server and client. This protocol sits on top of the TCP protocol.

When you have a protocol, there are often still implementation choices for the recv() code. The simplest apporach is often to use a MSG_WAIT_FOR_ALL to receive a fixed length header, then a second MSG_WAIT_FOR_ALL to retrieve the additional bytes of data that the header promises. For very large messages there can be buffering issues with that as mentioned in my earlier comment.

If you are embedding a length at the front of a message you're sending, the simplest approach is probably to write it as a fixed-width numeric field, as in:

const char* p = asprintf("%06d%s", message_length, message_data);

Then the retriever can say:

char header[6 + 1];
header[6] = '\0';  // make sure it's NUL terminated as per C ASCIIZ string conventions
if (recv(client_desc,header,sizeof header,MSG_WAITALL) == sizeof header)
{
    int message_size = atoi(header);
    char* buff = malloc(message_size);
    if (recv(client_desc, buff, message_size, MSG_WAITALL) == message_size)
    {
         // use the message in buff...

    }
    else
        fprintf(stderr, "couldn't retrieve all the message body\n");
}
else
     fprintf(stderr, "couldn't retrieve all the message header\n"); 

With that approach, the messages themselves might look like "000005hello" or "000011hello world". The count could optionally include the bytes in the header. Many protocols use a 2's complement encoding of numbers such as the message length - you can use hton and ntoh to standardise the byte order across a heterogeneous collection of big- and little-endian machines, just as you're probably already doing for the TCP port number in your sockaddr_in structures, then write(descriptor, &my_32bits, sizeof(my_32bits)).

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • Thank you very much for your reply! I have tried and used select() in the client to keep the process looking for the server reply, but the ISSET condition of the client socket never becomes true, and the program continues even though I set NULL as the timeout parameter. – Guillermo Gruschka May 15 '12 at 03:27
  • Given you obviously want the client to block waiting for the server reply, there's no need to use `select()` - just block in a `read()` or `recv()` call until the data arrives. It's noteworthy that TCP is also a byte-stream protocol, which means you should loop calling `read`/`recv` until you get as much data as you need for one logical message - it could arrive in several chunks but `read` and `recv` are designed to deliver between 1 byte and the number requested as a successful outcome to any single call. – Tony Delroy May 15 '12 at 03:45
  • That said - `select()` should work... did you use `FD_SET` to add the client's socket descriptor to the set of descriptors to wait for? Another common issue is not setting the first argument to select - the number of file descriptors - it's a bit sloppy, but you can pass `FD_SETSIZE` to guarantee you're covering all the descriptors. – Tony Delroy May 15 '12 at 03:47
  • thanks again. Yes, I added the client and server descirptors to the set, and used select this way select(max_descriptor,read_descriptors,NULL,NULL). So the recv doesn't get the whole stream at once? even if i PASS MSG_WAITALL ? – Guillermo Gruschka May 15 '12 at 04:04
  • Could you append a minimal copy of your code to the question? - then we can try to spot the `select()` issue. Re MSG_WAITALL - you could use it, but beware of buffering issues for large messages (e.g. http://stackoverflow.com/questions/8470403/socket-recv-hang-on-large-message-with-msg-waitall). – Tony Delroy May 15 '12 at 05:28
  • Sure, the problem is that im using a custom made shared library for it's just a part of a larger project in which I'll have to use sockets again. Here are two links, one only to the shared library (rppsocket.h) and anotherone to the client-server applications, library: http://www.mediafire.com/?imuf1inblt032jf ; IPCTest : http://www.mediafire.com/?4neg9449zoop6x9 . I very much appreciate your help and time!! – Guillermo Gruschka May 15 '12 at 14:02
  • I editted the question adding the funcition i designed to receive non fixed messages, allocating the buffer with MSG_PEEK. It never reallocates, the message is cut at 10 bytes, which is the initial value i set. – Guillermo Gruschka May 16 '12 at 17:14
  • Please see the discussion I added to my answer. – Tony Delroy May 17 '12 at 05:34