Search code examples
c++stringsocketstcp

How can I properly use recv() function to read and store client input


string readMessage(SOCKET clientSocket)
{
    char buf[4096];
    string storeInfo;
    ZeroMemory(buf, 4096);

    // Wait for the "Enter" key to be pressed
    while (true)
    {
        int bytes = recv(clientSocket, buf, 4096, 0);
        if (bytes == SOCKET_ERROR)
        {
            cerr << "Error or invalid input, please try again!" << buf << endl;
            continue; // Restart the loop
        }

        for (int i = 0; i < bytes; i++)
        {
            storeInfo += buf[i];
            if (buf[i] == 13) // 13 is the ASCII code for "Enter"
            {
                cout << " input:-" << storeInfo << "----"  << endl;
                
                ZeroMemory(buf, 4096); storeInfoPtr = &storeInfo;
                return storeInfo; 
                storeInfo = "";

            }
        }
    }

I'm using the above code to prompt the client to provide input, then read it using the 'recv()' function, and store the data into a buffer. Then transfer the contents to string variable called storeInfo. According to the console output, the program will transfer the content of the buffer to the content of the string, and output it accurately, except for 2 issues.

  1. the formatting of the console is not correct, as shown below.
  2. the data in the string variable is not recognized at all by using the pointer storeInfoPtr to check its contents; initially it will print the correct text, which is just "placeholder," but after that it remains empty

CONSOLE OUTPUT

DESKTOP-AFNABTM.lan connected on port 2282
----ut:- √▼ √  √↑ √' ²☺ √♥ ²♥finley 
bad username!
----ut:-fishsticks
bad username!
----ut:-falcon
bad username!
----ut:-falcon
bad username!
----ut:-p3anutbutt3r
bad username!



notice the odd appearance of the first line of text, and the presence of unsual shapes and characters. also notice the formatting is completely wrong: the word "input" is cut out and the hyphens that are supposed to appear after the 'storeInput' data are misplaced

TERMINAL INPUT

please enter a username:
                         placeholderfinley
bad username
             please enter a username:
                                      fishsticks
bad username
             please enter a username:
                                      falcon
bad username
             please enter a username:
                                      falcon
bad username
             please enter a username:
                                      p3anutbutt3r
bad username
             please enter a username:

Notice the 'placeholder' text correctly appears in the first instance, but after updating the variable with some input, it becomes blank

Tried multiple strategies including using a pointer variable to try to store the data in another way and access it in different points of the program


Solution

  • I see a number of problems with your code.

    • You are looking for a specific byte, but you are reading whole blocks of data into a byte[] array. If the peer sends more data after the 0x0D ('\r') byte, you are throwing away the excess data instead of caching it for further processing.

    • If recv() fails for any error other than WSAEWOULDBLOCK, your code will get stuck in an endless loop. It will also get stuck in an endless loop if the peer disconnects gracefully, as recv() will return 0 in that case, not SOCKET_ERROR, which you are not handling.

    • Appending characters to a std;;string 1 char at a time is very inefficient, unless you reserve() space for it ahead of time.

    • You have an external variable named storeInfoPtr which you are pointing to the local storeInfo before return'ing to the caller. However, storeInfo will be destroyed upon function exit, leaving storeInfoPtr dangling.

    Try something more like this instead:

    string readMessage(SOCKET clientSocket)
    {
        char buf[4096], ch;
        int inBuf = 0, numRead;
        string storeInfo;
    
        // Wait for the "Enter" key to be pressed
        while (true)
        {
            numRead = recv(clientSocket, &ch, 1, 0);
            if (numRead <= 0)
            {
                if (numRead == SOCKET_ERROR)
                    cerr << "Read error " << WSAGetLastError() << "!" << endl;
                return ""; or throw an exception
            }
    
            if (ch == '\r')
            {
                storeInfo.append(buf, inBuf);
                break;
            }
     
            if (inBuf == sizeof(buf))
            {
                storeInfo.append(buf, inBuf);
                inBuf = 0;
            }
    
            buf[inBuf++] = ch;
        }
    
        cout << " input:-" << storeInfo << "----"  << endl;
                    
        //storeInfoPtr = &storeInfo;
        return storeInfo; 
    }
    

    Note, most systems treat Enter as 0x0A ('\n') instead of 0x0D. If your client doesn't, you should design your protocol so it does. Most common text-based Internet protocols actually send 0x0D 0x0A instead, regardless of platform.

    In which case, try this:

    string readMessage(SOCKET clientSocket)
    {
        char buf[4096], ch;
        int inBuf = 0, numRead;
        string storeInfo;
    
        // Wait for the "Enter" key to be pressed
        while (true)
        {
            numRead = recv(clientSocket, &ch, 1, 0);
            if (numRead <= 0)
            {
                if (numRead == SOCKET_ERROR)
                    cerr << "Read error " << WSAGetLastError() << "!" << endl;
                return ""; or throw an exception
            }
    
            if (ch == '\n')
            {
                if ((inBuf > 0) && (buf[inBuf-1] == '\r'))
                    --inBuf;
                storeInfo.append(buf, inBuf);
                break;
            }
     
            if (inBuf == sizeof(buf))
            {
                if (buf[inBuf-1] == '\r')
                {   
                    storeInfo.append(buf, inBuf-1);
                    buf[0] = `'\r';
                    inBuf = 1;
                }
                else
                {
                    storeInfo.append(buf, inBuf);
                    inBuf = 0;
                }
            }
    
            buf[inBuf++] = ch;
        }
    
        cout << " input:-" << storeInfo << "----"  << endl;
                    
        //storeInfoPtr = &storeInfo;
        return storeInfo; 
    }