Cory Gross
Cory Gross

Reputation: 37206

Copying from stringstream to char * array return as function parameter

I am experimenting with the HTTP protocol using Winsock2. I am working on a function

int recv_data(const char *hostname, char *resp);

The function is meant to send an HTTP HEAD request to a given host and then receive a response. It allocated memory at the pointer resp and copies the response there before returning the total number of bytes received for the response.

Here is my recieve loop:

int recv_data(const char *hostname, char *resp)
{
    int totalRecvd = 0;
    stringstream sStream;
    while (true)
    {
        char buffer[MAX_HEADER_SIZE];
        int retValue = recv(s, buffer, MAX_HEADER_SIZE, 0);
        if (retValue == 0)
            break;  // connection has been closed
        else if (retValue == SOCKET_ERROR)
            throw RecvException("http_headreq() unable to receive data");
        else    //
        {   
            buffer[retValue] = 0; // append null terminator
            sStream << buffer;    // dump buffer into stream
            totalRecvd += retValue + 1; // tally received bytes
        }
    }

    /** Allocate and read entire read stream into memory */
    resp = new char[totalRecvd + 1];
    strcpy(resp, sStream.str().c_str());
    return totalRecvd);
}

All of this works just fine and all and if I output resp at this point it outputs just fine. I just have a problem if I try to output resp after the function has returned apparently. I do not believe this should be how things normally go and if I am not mistaken I believe it has something to do with me using the stringstream to temporarily store the response. I think I have read somewhere about the data that stringstream collects going out of scope.

I was hoping that I could have this function set up this way where the caller can just pass in a char* and the function will allocate the correct amount of memory (which is determined at runtime depending on the host and the number of bytes returned by recv(). Is there anyway for me to get a permanent copy from a stringstream in memory as a char array with the pointer being bad after the function returns and the stringstream goes out of scope?

[EDIT]: Here is the solution posted below incorporated into my problem, anyone looking to reuse this for Winsock2 proramming have at it, seems to work well. Will recv data from the server until the connection is closed when recv() returns 0. The solution is passing in a reference to the pointer, because the new operator changes the pointer and that change is not reflected after the function returns unless it is passed in by reference.

int recv_data(SOCKET s, char *&data)
{
    int totalRecvd = 0;
    std::stringstream sStream;
    while (true)
    {
        char buffer[MAX_HEADER_SIZE];
        int retValue = recv(s, buffer, MAX_HEADER_SIZE, 0);
        if (retValue == 0)
            break;  // connection has been closed
        else if (retValue == SOCKET_ERROR)
            throw RecvException("http_headreq() unable to receive data");
        else    //
        {   
            buffer[retValue] = 0; // append null terminator
            sStream << buffer;    // dump buffer into stream
            totalRecvd += retValue + 1; // tally received bytes
        }
    }

    /** Allocate and read entire read stream into memory */
    data = new char[totalRecvd + 1];
    strcpy_s(data, totalRecvd, sStream.str().c_str());
    data[totalRecvd] = 0;
    return totalRecvd;
}

Upvotes: 0

Views: 4677

Answers (2)

Puppy
Puppy

Reputation: 146940

If you alter your code to use std::string and boost::asio, you will no longer suffer from memory management problems.

Upvotes: 0

Robᵩ
Robᵩ

Reputation: 168626

resp is a local variable in the http_req function. Updating the value of resp will have no effect outside of http_req. This line:

resp = new char[totalRecvd + 1];

will have only local effect.

Try this signature:

int http_req(const char *hostname, char *&resp);


Even better, try returning the data in a C++ way:

std::string http_req(const std::string& hostname) {
    ...
    return sStream.str()
}

Upvotes: 3

Related Questions