Problem with recv() on a tcp connection

Michael picture Michael · Mar 6, 2011 · Viewed 10.3k times · Source

I am simulating TCP communication on windows in C. I have sender and a receiver communicating.

The sender sends packets of specific size to the receiver. The receiver gets them and sends an ACK for each packet it received back to the sender. If the sender didn't get a specific packet (they are numbered in a header inside the packet) it sends the packet again to the receiver. Here is the getPacket function on the receiver side:

//get the next packet from the socket. set the packetSize to -1
//if it's the first packet.
//return: total bytes read
// return: 0 if socket has shutdown on sender side, -1 error, else number of bytes received
int getPakcet(char* chunkBuff, int packetSize, SOCKET AcceptSocket)
{
    int totalChunkLen = 0;
    int bytesRecv = -1;
    bool firstTime = false;

    if(packetSize == -1)
    {
        packetSize = MAX_PACKET_LENGTH;
        firstTime = true;
    }

    int needToGet = packetSize;

    do
    {
        char* recvBuff;
        recvBuff = (char*)calloc(needToGet, sizeof(char));

        if(recvBuff == NULL)
        {
            fprintf(stderr, "Memory allocation problem\n");
            return -1;
        }

        bytesRecv = recv(AcceptSocket, recvBuff, needToGet, 0);

        if(bytesRecv == SOCKET_ERROR)
        {
            fprintf(stderr, "recv() error %ld.\n", WSAGetLastError());
            totalChunkLen = -1;
            return -1;
        }

        if(bytesRecv == 0)
        {
            fprintf(stderr, "recv(): socket has shutdown on sender side");
            return 0;
        }
        else if(bytesRecv > 0)
        {
            memcpy(chunkBuff + totalChunkLen, recvBuff, bytesRecv);
            totalChunkLen += bytesRecv;
        }

        needToGet -= bytesRecv;
    }
    while((totalChunkLen < packetSize) && (!firstTime));

    return totalChunkLen;
}

I use firstTime because for the first time the receiver doesn't know the normal package size that the sender is going to send to it, so I use a MAX_PACKET_LENGTH to get a package and then set the normal package size to the number of bytes I have received.

My problem is the last package. It's size is less than the package size. So lets say last package size is 2 and the normal package size is 4. So recv() gets two bytes, continues to the while condition, then totalChunkLen < packetSize because 2<4 so it iterates the loop again and the gets stuck in recv() because it's blocking because the sender has nothing to send.

On the sender side I can't close the connection because I didn't get ACK back, so it's kind of a deadlock. The receiver is stuck because it's waiting for more packages but sender has nothing to send.

I don't want to use a timeout for recv() or to insert a special character to the package header to mark that it is the last one.

What can I do?

Answer

BertV picture BertV · Mar 6, 2011

The concept to keep in mind with low-level socket programming is that you are exchanging a bunch of bytes with no structure imposed by the transport. It is up to you to implement a protocol that does message delineation, either by putting the total length of what you consider a "message" at the start, by using a delimiter byte or sequence which you check in the received buffer, or by closing the connecting at the end (the latter looks easiest but is not the best solution, as you will want to reuse the connection in a real-world program as setting it up is expensive).

If this looks to complicated (and it is indeed not always easy), you need to look for a library that encapsulates this work for you, for example allowing you to send and receive an object which will be serialized, delineated and deserialized by the library code. But the work needs to be done and it will not be the transport layer doing it for you.

One small remark about the code shown: Your creating a memory leak with your multiple receive buffer allocations...