C++ – How to recive more than 65000 bytes in C++ socket using recv()

c++linuxserversocketsocketstcp

I am developing a client server application (TCP) in Linux using C++. I want to send more than 65,000 bytes at the same time. In TCP, the maximum packet size is 65,535 bytes only.

How can I send the entire bytes without loss?

Following is my code at server side.

//Receive the message from client socket
if((iByteCount = recv(GetSocketId(), buffer, MAXRECV, MSG_WAITALL)) > 0) 
{
     printf("\n Received bytes %d\n", iByteCount);

     SetReceivedMessage(buffer);
     return LS_RESULT_OK;
}

If I use MSG_WAITALL it takes a long time to receive the bytes so how can I set the flag to receive more than 1 million bytes at time.

Edit: The MTU size is 1500 bytes but the absolute limitation on TCP Packet size if 65,535.

Best Solution

Judging from the comments above, it seems you don't understand how recv works, or how it is supposed to be used.

You really want to call recv in a loop, until either you know that the expected amount of data has been received or until you get a "zero bytes read" result, which means the other end has closed the connection. Always, no exceptions.

If you need to do other things concurrently (likely, with a server process!) then you will probably want to check descriptor readiness with poll or epoll first. That lets you multiplex sockets as they become ready.

The reason why you want to do it that way, and never any different, is that you don't know how the data will be packeted and how (or when) packets will arrive. Plus, recv gives no guarantee about the amount of data read at a time. It will offer what it has in its buffers at the time you call it, no more and no less (it may block if there's nothing, but then you still don't have a guarantee that any particular amount of data will be returned when it resumes, it may still return e.g. 50 bytes!).

Even if you only send, say, 5,000 bytes total, it is perfectly valid behaviour for TCP to break this into 5 (or 10, or 20) packets, and for recv to return 500 (or 100, or 20, or 1) bytes at a time, every time you call it. That's just how it works.
TCP guarantees that anything you send will eventually arrive at the other end or produce an error. And, it guarantees that whatever you send arrives in order. It does not guarantee much else. Above all, it does not guarantee that any particular amount of data is ready at any given time.
You must be prepared for that, and the only way to do it is calling recv repeatedly. Otherwise you will always lose data under some circumstances.

MSG_WAITALL should in principle make it work the way you expect, but that is bad behaviour, and it is not guaranteed to work. If the socket (or some other structure in the network stack) runs against a soft or hard limit, it may not, and probably will not fulfill your request. Some limits are obscure, too. For example, the number for SO_RCVBUF must be twice as large as what you expect to receive under Linux, because of implementation details.

Correct behaviour of a server application should never depend on assumptions such as "it fits into the receive buffer". Your application needs to be prepared, in principle, to receive terabytes of data using a 1 kilobyte receive buffer, and in chunks of 1 byte at a time, if need be. A larger receive buffer will make it more efficient, but that's it... it still has to work either way.

The fact that you only seee failures upwards of some "huge" limit is just luck (or rather, bad luck). The fact that it apparently "works fine" up to that limit suggests what you do is correct, but it isn't. It's an unlucky coincidence that it works.

EDIT:
As requested in below comment, here is what this could look like (Code is obviously untested, caveat emptor.)

std::vector<char> result;
int size;

char recv_buf[250];

for(;;)
{
    if((size = recv(fd, recv_buf, sizeof(recv_buf), 0)) > 0)
    {
        for(unsigned int i = 0; i < size; ++i)
            result.push_back(recv_buf[i]);
    }
    else if(size == 0)
    {
        if(result.size() < expected_size)
        {
            printf("premature close, expected %u, only got %u\n", expected_size, result.size());
        }
        else
        {
            do_something_with(result);
        }
        break;
    }
    else
    {
        perror("recv");
        exit(1);
    }
}

That will receive any amount of data you want (or until operator new throws bad_alloc after allocating a vector several hundred MiB in size, but that's a different story...).

If you want to handle several connections, you need to add poll or epoll or kqueue or a similar functionality (or... fork), I'll leave this as exercise for the reader.

Related Question