0
votes

I want to receive a request that is being sent in multiple chunks. In order to do so, I am using a TCP socket with recv. I loop over recv as follows:

while ((total_recv < MAX_HEADER) && (received = recv(client, req_buffer, CHUNK, 0)) > 0) {
total_recv += received;
if (strstr(req_buffer, delimiter) != NULL)
    break;
}

My objective is to stop receiving when I have received the header whose end is marked by the delimiter. I am not guaranteed that the delimiter is present and so I break the loop if the total_recv is great or equal to the maximum known header size.

I do not like this. What bothers me is I can receive a packet with no delimiter that is smaller than the maximum header size. In this case I then have to assume that recv will continue to increment total_recv until the loop breaks. I know from reading the sockets API that recv will return the number of bytes read into the buffer, -1 on error, or 0 if the connection has been closed.

Is there a better way to loop on recv without relying on timeouts? What exactly are send() and recv() returning when a connection is open but not data is being transmitted by the sender or to the receiver?

2
"My objective is to stop receiving when I have received the header whose end is marked by the delimiter" - then you are likely going to have to read 1 char at a time until you reach the delimiter. If the size of the header is not an even multiple of CHUNK then your recv loop will read too much data once it reaches the end of the header. Also, your use of strstr() is broken since req_buffer is not null terminated, and you are not concatenating each req_buffer received into an accumulated buffer on each loop iteration.Remy Lebeau
"I am not guaranteed that the delimiter is present" - why not? What kind of protocol are you working with that it doesn't guarantee the delimiter is present? That would be very unusual, and very unstable, for a TCP-based protocol to not provide any way to reliably detect the end of the header. Are you sure the actual header size is not encoded inside the header itself? That would make more sense when a terminating delimiter is not used.Remy Lebeau

2 Answers

3
votes

If the socket is in blocking mode (the default), recv() will wait until there is data available to return. The only way to deal with this is with a timeout. The easiest way to do this is with select() or epoll().

If the socket is in non-blocking mode, recv() will return -1 immediately if there's nothing available, and errno will be set to EWOULDBLOCK. You'll still need to use some kind of timeout, since this could happen simply because you're calling recv() too soon, before all the data that has been sent is received.

Timeouts are a pretty fragile mechanism, because it's always possible that there can be temporary network errors that last longer than the timeout, and you'll mistakenly treat the data before the outage as complete.

Maybe you can redesign the protocol to handle this better. Rather than reading until a delimiter, have the sender send a prefix with the number of bytes in the message, then you call recv() until you get that many bytes.

1
votes

Yes there is improvement needed for your code.

  1. The reason you need a loop is that recv could return less than a full header, even if later in the header the delimiter will occur. So you have to loop back and read more.

    You have a problem in your code that the second time recv is called, it will read the second time into the beginning of your buffer.

  2. You need to test for EOF.

  3. If no data is being sent, recv will wait until there is some. If necessary it will wait forever, but it will return with a non-success if the socket is closed.