Sending data from C++ server to React.js client

41 Views Asked by At

I have a TCP server written in C++ and a React.js application on the client side.
All you need to know is that the send() method in <winsock.h> sends an empty string to the client instead of data as a string.

Сlass method that defines the logic for sending data to the client:

    void TcpServer::sendDataToClient()
    {
        nlohmann::json jsonData = readData();
        std::string jsonDataStr = jsonData.dump();

        if (jsonData.empty())
        {
            log("No data to send to client.\n\n");
            return;
        }

        std::string headers =
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: application/json; charset=utf-8\r\n"
            "Access-Control-Allow-Origin: *\r\n"
            "Content-Length: " + std::to_string(jsonDataStr.size()) + "\r\n"
            "Connection: close\r\n"
            "\r\n";

        send(m_new_socket, headers.c_str(), headers.size(), 0);
        send(m_new_socket, jsonDataStr.c_str(), jsonDataStr.size(), 0);

        log("------ Data sent to client ------\n\n");
    }

Buffer size and CORS:

namespace
{
    const int BUFFER_SIZE = 500000000;

    void log(const std::string& message)
    {
        std::cout << message << std::endl;
    }

    void exitWithError(const std::string& errorMessage)
    {
        std::cout << WSAGetLastError() << std::endl;
        log("ERROR: " + errorMessage);
        exit(1);
    }

    void setCorsHeaders(SOCKET clientSocket)
    {
        const char* headers =
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: application/json\r\n"
            "Access-Control-Allow-Origin: *\r\n"
            "Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS\r\n"
            "Access-Control-Allow-Headers: Content-Type\r\n"
            "Content-Length: 0\r\n"
            "Connection: close\r\n"
            "\r\n";

        send(clientSocket, headers, strlen(headers), 0);
    }
}

This is how I send a GET request to a c++ server:

  const handleFetchServer = async () => {
    try {
      const response = await axios.get('http://127.0.0.1:5174')
      console.log('Response:', response)
      console.log('Data from server:', response.data)
    } catch (error) {
      console.error('Error fetching data: ', error)
    }
  }

1. The data is exactly stored in the jsonDataStr variable
2. No compiler or linker errors
3. Everything works as it should, just when sending data to the client an empty string is sent instead of the data in the jsonDataStr variable
4. On the client I get an empty string - The GET request itself is executed with code 200, which means that the server responded and everything was successful.
5. I also tried passing a very small string from the server to the client but I again saw an empty string

1

There are 1 best solutions below

0
frozzen_alacrity On

I solved this problem.

The whole point is that I didn’t notice how GET requests are processed. In general, here is the previous function that processed requests:

void TcpServer::handleRequest(const std::string& request)
{
    if (request.find("GET /user_data.json HTTP/1.1") != std::string::npos)
    {
        //GET
        sendDataToClient();
    }
    else if (request.find("POST /user_data.json HTTP/1.1") != std::string::npos)
    {
        //POST
        std::cout << "POST\n\n" << std::endl;
        size_t start = request.find("{");
        if (start != std::string::npos)
        {
            std::string jsonData = request.substr(start);
            writeData(jsonData);
        }
    }
}

I had a test response to a GET request that I didn’t need, I did it at the very beginning to check the server’s response. And he was right in the endless listener loop:

void TcpServer::startListen()
{
    if (listen(m_socket, 20) < 0)
    {
        exitWithError("Socket listen failed");
    }

    std::ostringstream ss;
    ss << "\n*** Listening on ADDRESS: " << inet_ntoa(m_socketAddress.sin_addr) << " PORT: " << ntohs(m_socketAddress.sin_port) << " ***\n\n";
    log(ss.str());

    int bytesReceived;

    while (true)
    {
        log("====== Waiting for a new connection ======\n\n\n");
        acceptConnection(m_new_socket);

        char* buffer = new char[BUFFER_SIZE];

        bytesReceived = recv(m_new_socket, buffer, BUFFER_SIZE, 0);
        if (bytesReceived < 0)
        {
            exitWithError("Failed to receive bytes from client socket connection");
        }

        std::ostringstream ss;
        ss << "------ Received Request from client ------\n\n";
        log(ss.str());

        sendResponse();

        std::string requestData(buffer);
        handleRequest(requestData);

        delete[] buffer;
        closesocket(m_new_socket);
    }
}

And it was called:

sendResponse();

Therefore, when the client requested data from the server, the first thing he received was a response from this function, and then the data that was needed was sent, but since the client had already received the response, he simply did not take the following data.

In order to detect this problem I used the Wireshark utility.

Here is my modified request processing function:

void TcpServer::handleRequest(const std::string& request)
{
    if (request.find("GET /user_data.json HTTP/1.1") != std::string::npos)
    {
        //GET
        sendDataToClient();
    }
    else if (request.find("POST /user_data.json HTTP/1.1") != std::string::npos)
    {
        //POST
        std::cout << "POST\n\n" << std::endl;
        size_t start = request.find("{");
        if (start != std::string::npos)
        {
            std::string jsonData = request.substr(start);
            writeData(jsonData);
        }
    }
    else if (request.find("GET / HTTP/1.1") != std::string::npos)
    {
        sendResponse();
    }
    else
    {
        log("Unknown request.\n\n");
    }
}

And of course I made one call to the send() method to send data:

void TcpServer::sendDataToClient()
{
    nlohmann::json jsonData = readData();
    std::string jsonDataStr = jsonData.dump();
    std::cout << jsonDataStr.size() << "\n\n" << std::endl;
    if (jsonData.empty())
    {
        log("No data to send to client.\n\n");
        return;
    }

    std::string headers =
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: application/json; charset=utf-8\r\n"
        "Access-Control-Allow-Origin: *\r\n"
        "Content-Length: " + std::to_string(jsonDataStr.size()) + "\r\n"
        "Connection: close\r\n"
        "\r\n";

    send(m_new_socket, (headers + jsonDataStr).c_str(), headers.size() + jsonDataStr.size(), 0);

    log("------ Data sent to client ------\n\n");
}