c++winsock2

WinSock2 "recv" never reading all data


I'm trying to transmit a large chunk of data using WinSock2 from my Ubuntu Server (running wine) to my Windows Client.

This is the client code:

int expected_length = 0;
recv(m_Socket, (char*)&expected_length, 4, 0);

if (expected_length <= 0)
    return;

unsigned char* buffer = new unsigned char[expected_length];
int total_read = 0;

while (total_read < expected_length)
{
    int to_read = expected_length - total_read;
    int count = recv(m_Socket, (char*)buffer + total_read, to_read, 0);
    if (count <= 0) {
        delete[] buffer;
        return nullptr;
    }
    total_read += count;
}

This is the Server code:

send(client_sock, (const char*)&nsize, 4, 0);
int total_sent = 0;
const char* ptr = buffer.data;
while (total_sent < nsize)
{
    int rest = nsize - total_sent;
    int chunk = std::min(4096, rest);

    int sent = send(client_sock, ptr + total_sent, chunk, 0);
    if (sent < 0)
        break;

    std::cout << "sent: " << sent << ", total_sent: " << total_sent << std::endl;
    total_sent += sent;
}

Initialization for the Socket on the Client:

WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

m_Socket = socket(AF_INET, SOCK_STREAM, 0);
if (m_Socket < 0)
    return false;

sockaddr_in servAddr;
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(25010);
inet_pton(AF_INET, "127.0.0.1", &servAddr.sin_addr);

if (connect(m_Socket, (sockaddr*)&servAddr, sizeof(servAddr)) < 0)
    return false;

return true;

Now to my Issue:


Solution

  • You are not checking the return value of send() or recv() when exchanging the 4-byte data length. You need to make sure that all 4 bytes are being transmitted/received correctly. You already have logic to do that for the data buffer, just do it for the data length, too.

    Also, for good measure, you should use fixed-sized integer types for socket data, and send integers in network byte order (big endian), especially for consistency when crossing platform boundaries.

    Try something more like this:

    #ifndef WINDOWS
    using SOCKET = int;
    using TIMEVAL = struct timeval;
    #endif
    
    int lastSocketError()
    {
        #ifdef WINDOWS
        return WSAGetLastError();
        #else
        return errno;
        #endif
    }
    
    bool isNonBlockingSocketError(int err)
    {
        #ifdef WINDOWS
        return (err == WSAEWOULDBLOCK);
        #else
        return (err == EAGAIN || err == EWOULDBLOCK || err == EINTR);
        #endif
    }
    
    bool recv_all(SOCKET s, void *buffer, size_t length)
    {
        unsigned char *data = static_cast<unsigned char *>(buffer);
        size_t total_recv = 0;
    
        while (length > 0)
        {
            int result = recv(s, data, length, 0);
            if (result < 0)
            {
                int err = lastSocketError();
                if (!isNonBlockingSocketError(err))
                {
                    std::cout << "Unable to receive data, recv error " << err << '\n';
                    return false;
                }
    
                fd_set fd;
                FD_ZERO(&fd);
                FD_SET(s, &fd);
    
                TIMEVAL tv;
                tv.tv_sec = 5;
                tv.tv_usec = 0;
    
                result = select(s+1, &fd, nullptr, nullptr, &tv);
                if (result < 0)
                {
                    err = lastSocketError();
                    std::cout << "Unable to receive data, select error " << err << '\n';
                    return false;
                }
                else if (result == 0)
                {
                    std::cout << "Unable to receive data, timed out\n';
                    return false;
                }
            }
            else if (result == 0)
            {
                std::cout << "Unable to receive data, disconnected\n";
                return false;
            }
            else
            {
                total_recv += result;
                data += result;
                length -= result;
    
                std::cout << "recv: " << result << ", total_recv: " << total_recv << '\n';
            }
        }
    
        return true;
    }
    
    bool recv_int32(SOCKET s, int32_t *value)
    {
        bool result = recv_all(s, value, 4);
        if (result) *value = ntohl(*value);
        return result;
    }
    
    ...
    
    int32_t expected_length = 0;
    if (!recv_int32(m_Socket, &expected_length)) {
        // error handling ...
    }
    
    // allocate buffer to expected length...
    
    if (!recv_all(m_Socket, buffer, expected_length)) {
        // error handling ...
    }
    
    /* alternatively:
    
    unsigned char chunk[1024];
    while (expected_length > 0)
    {
        int chunk_size = std::min(expected_length, 1024);
        if (!recv_all(m_Socket, chunk, chunk_size)) {
            // error handling ...
        }
    
        // use chunk up to chunk_size as needed...
    
        expected_length -= chunk_size;
    }
    */
    
    bool send_all(SOCKET s, const void *buffer, size_t length)
    {
        const unsigned char *data = static_cast<const unsigned char *>(buffer);
        size_t total_sent = 0;
    
        while (length > 0)
        {
            int result = send(s, data, length, 0);
            if (result < 0)
            {
                int err = latSocketError();
                if (!isNonBlockingSocketError(err))
                {
                    std::cout << "Unable to send data, send error " << err << '\n';
                    return false;
                }
    
                fd_set fd;
                FD_ZERO(&fd);
                FD_SET(s, &fd);
    
                TIMEVAL tv;
                tv.tv_sec = 5;
                tv.tv_usec = 0;
    
                result = select(s+1, nullptr, &fd, nullptr, &tv);
                if (result < 0)
                {
                    err = lastSocketError();
                    std::cout << "Unable to send data, select error " << err << '\n';
                    return false;
                }
                else if (result == 0)
                {
                    std::cout << "Unable to send data, timed out\n';
                    return false;
                }
            }
            else
            {
                total_sent += result;
                data += result;
                length -= result;
    
                std::cout << "sent: " << result << ", total_sent: " << total_sent << '\n';
            }
        }
    
        return true;
    }
    
    bool send_int32(SOCKET s, int32_t value)
    {
        value = htonl(value);
        return send_all(s, &value, 4);
    }
    
    ...
    
    if (!send_int32(client_sock, nsize)) {
        // error handling ...
    }
    
    if (!send_all(client_sock, buffer.data, nsize)) {
        // error handling ...
    }