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:
- 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))
.