2

I am creating a server/client TCP in C.

The idea is for the server to send a relatively large amount of information. However, the buffer in the client has a size of only 512 (I don't want to increase this size), and obviously, the information sent by the server is larger than this. Let's imagine 812 bytes.

What I want to do is, in the client, read 512 bytes, print them on the client's console, and then read the remaining bytes, and print them as well.

Here's what should happen:

1) Create server, and block in the read() system call (waiting for the client to write something);

2) Create the client, and write something in the socket, and then blocks on read(), waiting for the server to respond;

3) The server's read() call returns, and now server has to send that large amount of data, using the following code (after creating a new process):

dup2(new_socketfd, STDOUT_FILENO); // Redirect to socket

execlp("/application", "application", NULL); // Application that prints the information to send to the client

Let's imagine "application" printed 812 bytes of data to the socket.

4) Now the client has to read 812 bytes, with a buffer size of 512. That's my problem.

How can I approach this problem? I was wondering if I could make a loop, and read until there's nothing to read, 512 by 512 bytes. But as soon as there's nothing to read, client will block on read().

Any ideas?

Steve Friedl
  • 3,929
  • 1
  • 23
  • 30
Alcachofra
  • 63
  • 6
  • Yes: you need a loop. Besides: the first read() can actually read *fewer* than 512 bytes! `But as soon as there's nothing to read, client will block on read()` Not if the sender close()s the connection. In that case the read() will return zero. – wildplasser May 27 '20 at 13:43
  • I cannot really understand your question. TCP is a **stream** protocol. That means that the packets sent from one side may be splitted or reassambled by the *network* (in the sense of all the drivers and relays between both sides). That means that a reader shall always use a loop if it does not want to be bitten by an incomplete fragment. If you want to have a concept of messages, use UDP (packet oriented) or use a higher level protocol to delimit the messages. – Serge Ballesta May 27 '20 at 13:45
  • Thanks for answering! But how should I set up the loop, then? When should it break out? When do I know I read everything? – Alcachofra May 27 '20 at 13:48
  • @wildplasser _Not if the sender close()s the connection. In that case the read() will return zero._ Yes, I know. But if I want to break out of the loop, I need to know when I've read everything I need. Everything the server sent. How can I implement this? – Alcachofra May 27 '20 at 14:03

1 Answers1

2

recv will block when there is no data in the stream. Any data extracted from the stream, the length is returned from recv. You can write a simple function to extract the full data just by using an offset variable and checking the return value.

A simple function like this will do.

ssize_t readfull(int descriptor,char* buffer, ssize_t sizetoread){
    ssize_t offset = 0;
    while (offset <sizetoread) {
        ssize_t read = recv(descriptor,buffer+offset,sizetoread-offset,0);
        if(read < 1){
            return offset;
        }
        offset+=read;
    }
    return offset;
}

Also servers typically send some kind of EOF when the data is finished. Either the server might first send the length of the message to be read which is a constant size either four or eight bytes, then it sends the data so you know ahead of time how much to read. Or, in the case of HTTP for example, there is the content-length field as well as the '\r\n' delimeters.

Realistically there is no way to know how much data the server has available to send you, it's impractical. The server has to tell you how much data there is through some kind of indicator.

Since you're writing the server yourself, you can first send a four byte message which can be an int value of how much data the client should read.

So your server can look like this:

int sizetosend = arbitrarysize;
send(descriptor,(char*)&sizetosend,sizeof(int),0);

send(descriptor,buffer,sizetosend,0);

Then on your client side, read four bytes then the buffer.

int sizetoread = 0;
ssize_t read = recv(descriptor,(char*)&sizetoread,sizeof(int),0);
if(read < 4)
  return;

//Now just follow the code I posted above
Irelia
  • 3,407
  • 2
  • 10
  • 31