Search code examples
c++servertcpclient

How to iterate a loop in a TCP client server using c++


I am new to TCP client/server communication. I am trying to send output in a loop in the server and to receive it in the client.

My problem is, I am getting random values as output in the client.

I have shared my server and client snippets:

server.cpp

string r_n, r_n1, r_n2, r_n3, r_n4, r_n5, r_n6, r_n7, r_n8, r_n9;

for (result::const_iterator c = R.begin(); c != R.end(); ++c) {

    r_n =  c[0].as<int>();
    r_n1 = c[1].as<string>();

    r_n2 = c[2].as<string>();
    r_n3 = c[3].as<string>();
    r_n4 = to_string(c[4].as<long long int>());
    r_n5 = c[5].as<string>();
    r_n6 = c[6].as<string>();
    r_n7 = c[7].as<string>();
    r_n8 = to_string(c[8].as<int>());
    r_n9 = to_string(c[9].as<int>());

    send(serverSocket, r_n.c_str(), sizeof(r_n), 0);
    send(serverSocket, r_n1.c_str(), sizeof(r_n1), 0);
    send(serverSocket, r_n2.c_str(), sizeof(r_n2), 0);
    send(serverSocket, r_n3.c_str(), sizeof(r_n3), 0);
    send(serverSocket, r_n4.c_str(), sizeof(r_n4), 0);
    send(serverSocket, r_n5.c_str(), sizeof(r_n5), 0);
    send(serverSocket, r_n6.c_str(), sizeof(r_n6), 0);
    send(serverSocket, r_n7.c_str(), sizeof(r_n7), 0);
    send(serverSocket, r_n8.c_str(), sizeof(r_n8), 0);
    send(serverSocket, r_n9.c_str(), sizeof(r_n9), 0);
}

client.cpp

int i = 0;
while (i < 4)
{
    recv(TCPClientSocket, RecvBuffer, sizeof(RecvBuffer), 0);
    recv(TCPClientSocket, RecvBuffer1, sizeof(RecvBuffer1), 0);
    recv(TCPClientSocket, RecvBuffer2, sizeof(RecvBuffer2), 0);
    recv(TCPClientSocket, RecvBuffer3, sizeof(RecvBuffer3), 0);
    recv(TCPClientSocket, RecvBuffer4, sizeof(RecvBuffer4), 0);
    recv(TCPClientSocket, RecvBuffer5, sizeof(RecvBuffer5), 0);
    recv(TCPClientSocket, RecvBuffer6, sizeof(RecvBuffer6), 0);
    recv(TCPClientSocket, RecvBuffer6, sizeof(RecvBuffer6), 0);
    recv(TCPClientSocket, RecvBuffer7, sizeof(RecvBuffer7), 0);
    recv(TCPClientSocket, RecvBuffer8, sizeof(RecvBuffer8), 0);
    recv(TCPClientSocket, RecvBuffer9, sizeof(RecvBuffer9), 0);

    cout << "\t" << RecvBuffer << "\t" << RecvBuffer1 << "\t" << RecvBuffer2 << "\t" << RecvBuffer3;
    cout << "\t" << RecvBuffer4 << "\t" << RecvBuffer5 << "\t" << RecvBuffer6 << "\t" << RecvBuffer7 << "\t" << RecvBuffer8 << "\t" << RecvBuffer9 << endl;
    cout << endl;

    i++;
}

output in client:

102     AC      First   shek 9944833930      Chennai      2022-05-21      6       1000

Solution

  • TCP is a byte stream, it has no concept of message boundaries, so you have to be explicit about data sizes in the data itself. Either by sending the data's size before sending the data itself, or by sending a unique terminator after the data.

    Your string data is variable-length, but you are not delimiting your strings in any way, so the client has no way to know how much data is actually being sent, or even which data belongs to which string.

    When sending a string via send(), sizeof() is the wrong buffer size to use. You need to use the string's size() method instead. You also need to pay attention to send()'s return value, as it tells you how many bytes were actually accepted for sending, which may be (and frequently is) less than the number of bytes requested. Same with recv(). So, you need to call both functions in loops until you have sent/received the expected number of bytes. Also, the output from recv() is not null-terminated, but your code is expecting it to be.

    With that said, try something more like this instead:

    server.cpp

    void sendRaw(int socket, const void *data, size_t size)
    {
        const char *ptr = reinterpret_cast<const char*>(data); 
        while (size > 0)
        {
            int numSent = send(socket, ptr, size, 0);
            if (numSent < 0) throw ...;
            ptr += numSent;
            size -= numSent;
        }
    }
    
    void sendUInt32(int socket, uint32_t value)
    {
        value = htonl(value);
        sendRaw(socket, &value, sizeof(value));
    }
    
    void sendString(int socket, const string &str)
    {
        uint32_t size = str.size();
        sendUInt32(socket, size);
        sendRaw(socket, str.c_str(), size);
    }
    
    ...
    
    string n_r[10];
    
    sendUInt32(serverSocket, R.size());
    
    for (result::const_iterator c = R.begin(); c != R.end(); ++c) {
    
        n_r[0] = to_string(c[0].as<int>();
        n_r[1] = c[1].as<string>();
        n_r[2] = c[2].as<string>();
        n_r[3] = c[3].as<string>();
        n_r[4] = to_string(c[4].as<long long int>());
        n_r[5] = c[5].as<string>();
        n_r[6] = c[6].as<string>();
        n_r[7] = c[7].as<string>();
        n_r[8] = to_string(c[8].as<int>());
        n_r[9] = to_string(c[9].as<int>());
    
        for(int i = 0; i < 10; ++i) {
            sendString(serverSocket, n_r[i]);
        }
    }
    

    client.cpp

    void recvRaw(int socket, void *data, size_t size)
    {
        char *ptr = reinterpret_cast<char*>(data); 
        while (size > 0)
        {
            int numRecvd = recv(socket, ptr, size, 0);
            if (numRecvd <= 0) throw ...;
            ptr += numRecvd;
            size -= numRecvd;
        }
    }
    
    uint32_t recvUInt32(int socket)
    {
        uint32_t value;
        recvRaw(socket, &value, sizeof(value));
        return ntohl(value);
    }
    
    string recvString(int socket)
    {
        string str;
        uint32_t size = recvUInt32(socket);
        if (size > 0)
        {
            str.resize(size);
            recvRaw(socket, str.data(), size);
        }
        return str;
    }
    
    ...
    
    string r_n[10];
    uint32_t count = recvUInt32(TCPClientSocket);
    
    for(uint32_t i = 0; i < count; ++i)
    {
        for (int i = 0; i < 10; ++i) {
            r_n[i] = recvString(TCPClientSocket);
        }
    
        for (int i = 0; i < 10; ++i) {
            cout << '\t' << r_n[i];
        }
        cout << endl << endl;
    }
    

    That being said, some of your output fields were originally integers and not strings, so it would be better to send them as integers and not as strings, eg:

    server.cpp

    void sendRaw(int socket, const void *data, size_t size)
    {
        const char *ptr = reinterpret_cast<const char*>(data); 
        while (size > 0)
        {
            int numSent = send(socket, ptr, size, 0);
            if (numSent < 0) throw ...;
            ptr += numSent;
            size -= numSent;
        }
    }
    
    void sendUInt32(int socket, uint32_t value)
    {
        value = htonl(value);
        sendRaw(socket, &value, sizeof(value));
    }
    
    void sendInt32(int socket, int32_t value)
    {
        value = htonl(value);
        sendRaw(socket, &value, sizeof(value));
    }
    
    void sendInt64(int socket, int64_t value)
    {
        value = htonll(value); // or whatever equivalent your environment provides
        sendRaw(socket, &value, sizeof(value));
    }
    
    void sendString(int socket, const string &str)
    {
        uint32_t size = str.size();
        sendUInt32(socket, size);
        sendRaw(socket, str.c_str(), size);
    }
    
    ...
    
    int n_r, n_r8, n_r9;
    string n_r1, n_r2, n_r3, n_r5, n_r6, n_r7;
    long long int n_r4;
    
    sendUInt32(serverSocket, R.size());
    
    for (result::const_iterator c = R.begin(); c != R.end(); ++c) {
    
        n_r  = c[0].as<int>();
        n_r1 = c[1].as<string>();
        n_r2 = c[2].as<string>();
        n_r3 = c[3].as<string>();
        n_r4 = c[4].as<long long int>();
        n_r5 = c[5].as<string>();
        n_r6 = c[6].as<string>();
        n_r7 = c[7].as<string>();
        n_r8 = c[8].as<int>();
        n_r9 = c[9].as<int>();
    
        sendInt32(serverSocket, n_r);
        sendString(serverSocket, n_r1);
        sendString(serverSocket, n_r2);
        sendString(serverSocket, n_r3);
        sendInt64(serverSocket, n_r4);
        sendString(serverSocket, n_r5);
        sendString(serverSocket, n_r6);
        sendString(serverSocket, n_r7);
        sendInt32(serverSocket, n_r8);
        sendInt32(serverSocket, n_r9);
    }
    

    client.cpp

    void recvRaw(int socket, void *data, size_t size)
    {
        char *ptr = reinterpret_cast<char*>(data); 
        while (size > 0)
        {
            int numRecvd = recv(socket, ptr, size, 0);
            if (numRecvd <= 0) throw ...;
            ptr += numRecvd;
            size -= numRecvd;
        }
    }
    
    uint32_t recvUInt32(int socket)
    {
        uint32_t value;
        recvRaw(socket, &value, sizeof(value));
        return ntohl(value);
    }
    
    int32_t recvInt32(int socket)
    {
        int32_t value;
        recvRaw(socket, &value, sizeof(value));
        return ntohl(value);
    }
    
    int64_t recvInt64(int socket)
    {
        int32_t value;
        recvRaw(socket, &value, sizeof(value));
        return ntohll(value); // or whatever equivalent your environment provides
    }
    
    string recvString(int socket)
    {
        string str;
        uint32_t size = recvUInt32(socket);
        if (size > 0)
        {
            str.resize(size);
            recvRaw(socket, str.data(), size);
        }
        return str;
    }
    
    ...
    
    int n_r, n_r8, n_r9;
    string n_r1, n_r2, n_r3, n_r5, n_r6, n_r7;
    long long int n_r4;
    
    uint32_t count = recvUInt32(TCPClientSocket);
    
    for(uint32_t i = 0; i < count; ++i)
    {
        n_r  = recvInt32(TCPClientSocket);
        n_r1 = recvString(TCPClientSocket);
        n_r2 = recvString(TCPClientSocket);
        n_r3 = recvString(TCPClientSocket);
        n_r4 = recvInt64(TCPClientSocket);
        n_r5 = recvString(TCPClientSocket);
        n_r6 = recvString(TCPClientSocket);
        n_r7 = recvString(TCPClientSocket);
        n_r8 = recvInt32(TCPClientSocket);
        n_r9 = recvInt32(TCPClientSocket);
    
        cout << '\t' << n_r << '\t' << n_r1 << '\t' << n_r2 << '\t' << n_r3 << '\t' << n_r4 << '\t' << n_r5 << '\t' << n_r6 << '\t' << n_r7 << '\t' << n_r8 << '\t' << n_r9 << endl << endl;
    }