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;
}
TCP is a peer-to-peer protocol - after the server accept
s a client connect
ion, 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:
recv()
code knows the send()
code always sends some specific number of bytesThese 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))
.