C++ boost ptree segmentation fault in multiplayer server with Unity client

78 Views Asked by At

I am building a complete multiplayer application with 2 servers of nodejs and C++, the problem in the C++ server is that as soon as a client from the Unity client connects and sends the userdata, the server has a segmentation fault and exits without any messages.

I am using HTTP TCP protocol for communication and using the code below as the main function

int main() {
    boost::asio::io_context io_context;

    std::string raw_ip_address = "0.0.0.0";
    boost::system::error_code ec;

    boost::asio::ip::address ip_address =
        boost::asio::ip::address::from_string(raw_ip_address, ec);

    if (ec.value() != 0) {
        // Provided IP address is invalid. Breaking execution.
        std::cout
            << "Failed to parse the IP address. Error code = "
            << ec.value() << ". Message: " << ec.message();
        return ec.value();
    }
    tcp::endpoint tp(ip_address, 8081);

    tcp::acceptor acceptor(io_context, tp);

    Rooms MainRooms;

    while (true) {
        tcp::socket socket(io_context);
        acceptor.accept(socket);

        RouteRequest(socket, MainRooms);
    }
    return 0;
}

I cannot recreate this problem with postman.

void RouteRequest(tcp::socket& socket, Rooms& rooms) {
    std::string route = "/";
    std::string Method;
    //this is always set to process this 
    //better to use json everywhere
    //easier to hack too
    //hopefully security stuff can work here
    std::string request_data = BufferToString(socket, route, Method);

    //std::cout << Method << "THis is method" << std::endl;
    //std::cout << route << "THis is Route" << std::endl;
    //std::cout << request_data << std::endl;+      Current {username="" accessToken="" Room=0 ...} Player

    //always send a response
    std::string response;

    if (Method.compare("POST") == 0) {
        if (route.compare("/") == 0) {
            try
            {
                Player Current = Player(request_data);
                if (rooms.addRoomMate(Current)) {
                    sendHttpResponse(response, 200, "text/html", "Person Added!");
                    Current.printDetails();
                }
                else {
                    sendHttpResponse(response, 200, "text/html", "Person Exists!");
                }
            }
            catch (const std::exception& e)
            {
                throw e.what();
                std::cout << e.what() << std::endl;
                sendHttpResponse(response, 500, "text/html", e.what());
            }
        }

It seems that sometimes the request_data does not have it's json

I tried to check the values before sending , during transit and after reaching the C++ server . Before Sending : in the C# client the code is :

private IEnumerator Post(string url, string bodyJsonString)
    {
        var request = new UnityWebRequest(URL + url, "POST");
        byte[] bodyRaw = Encoding.UTF8.GetBytes(bodyJsonString);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);

        //Debug.Log(Encoding.ASCII.GetString(bodyRaw));

        request.downloadHandler = new DownloadHandlerBuffer();

        request.SetRequestHeader("Content-Type", "application/json");
        yield return request.SendWebRequest();

        //Debug.Log("Status Code: " + request.responseCode);
        //Debug.Log(request.downloadHandler.text);
    }
    private void SendWithDetails()
    {
        SendData();
        string json = JsonUtility.ToJson(ThisData);
        Debug.Log("Sending");
        Debug.Log(json);

       StartCoroutine(Post("update", json));
    }
    private void FixedUpdate()
    {
        if (isLoggedIn == true)
        {
            SendWithDetails();
        }
    }
    void SendData()
    {
        //we just need w and y
        float x =ThisPlayer.transform.position.x;
        float y = ThisPlayer.transform.position.y;
        float z = ThisPlayer.transform.position.z;
        float v = ThisPlayer.transform.eulerAngles.x;
        float u = ThisPlayer.transform.eulerAngles.y;
        float w = ThisPlayer.transform.eulerAngles.z;

        x = Mathf.FloorToInt(1000 * x) / 1000;
        y = Mathf.FloorToInt(1000 * y) / 1000;
        z = Mathf.FloorToInt(1000 * z) / 1000;
        u = Mathf.FloorToInt(1000 * u) / 1000;
        v = Mathf.FloorToInt(1000 * v) / 1000;
        w = Mathf.FloorToInt(1000*x)/1000;

        Debug.Log("Added data");
        ThisData.QuickAssign(x, y, z, u, v, w);
    }

I checked that all the variable and the byte array are perfectly correct.

Then in transit I checked it withe Wireshark, No problem there. The function that I am using to get the json string from the socket is

std::string BufferToString(tcp::socket& socket, std::string& route, std::string& Method) {

    boost::asio::streambuf request_buffer;
    boost::system::error_code error;

    try
    {
        boost::asio::read_until(socket, request_buffer, "\r\n\r\n", error);
        if (error) {
            throw std::runtime_error(error.message()); // Rethrow the error as an exception
        }
    }
    catch (const std::exception& ex)
    {
        std::cout << "Exception occurred: " << ex.what() << std::endl;
        return " ";
    }

    std::string request_data(boost::asio::buffers_begin(request_buffer.data()),
        boost::asio::buffers_end(request_buffer.data()));

    std::size_t path_start = request_data.find(" ");
    if (path_start != std::string::npos) {
        std::size_t path_end = request_data.find(" ", path_start + 1);
        route = request_data.substr(path_start + 1, path_end - path_start - 1);
    }
    else {
        std::cout << "String error: \n" << request_data << std::endl;
    }

    Method = request_data.substr(0, request_data.find(" "));

    return request_data;
}
1

There are 1 best solutions below

0
On

The std::string function find() was giving an unnatural value to the start_pos variable.Also the request_data would have an empty body. Instead of that I used the code:

std::string BufferToString(tcp::socket& socket, std::string& route, std::string& Method) {

boost::beast::flat_buffer request_buffer;
boost::beast::http::request<boost::beast::http::string_body> request;
boost::system::error_code error;

try
{
    boost::beast::http::read(socket, request_buffer, request, error);
    if (error) {
        throw std::runtime_error(error.message()); // Rethrow the error as an exception
    }
}
catch (const std::exception& ex)
{
    std::cout << "Exception occurred: " << ex.what() << std::endl;
    return " ";
}

Method = request.method_string();
route = request.target();

std::string request_data = request.body();
return request_data;}

This uses the in-built parser and handles whatever memory problem that I might've been facing ( which I couldn't figure out )