Search code examples
c++socketssfml

Sending data using TcpSocket in SFML causes segmentation fault


I'm using SFML's TcpSocket class to send and receive data, but for some reason I keep getting the error Segmentation fault: 11. Here's the server class:

#include "ServerGame.hpp"

ServerGame::ServerGame()
{

}

void ServerGame::sendData(char data[3][3])
{

    if (client.send(data, 9) != sf::Socket::Done)
    {
        //std::cout << "error" << std::endl;
    }

}

void ServerGame::connect()
{
    sf::TcpListener listener;

    // bind the listener to a port
    if (listener.listen(666) != sf::Socket::Done)
    {
        std::cout << "error" << std::endl;
    }

    // accept a new connection
    if (listener.accept(client) != sf::Socket::Done)
    {
        std::cout << "error" << std::endl;
    }
}

char** ServerGame::receiveData()
{
    char **data;
    data = new char*[3];
    for(int i = 0; i < 3; ++i)
    {
        data[i] = new char[3];
    }
    std::size_t received;

    // TCP socket:
    if (client.receive(data, 9, received) != sf::Socket::Done)
    {
        // error...
    }
    return data;
} 

Here's the client class:

#include "ClientGame.hpp"
#include <iostream>

ClientGame::ClientGame()
{

}

void ClientGame::connect()
{
    sf::Socket::Status status = socket.connect("127.0.0.1", 666);
    if (status != sf::Socket::Done)
    {
        // error...
    }
}

void ClientGame::sendData(char data[3][3])
{
    if(socket.send(data, 9) != sf::Socket::Done)
    {
        //error...
    }
}

char** ClientGame::receiveData()
{
    char **data;
    data = new char*[3];
    for(int i = 0; i < 3; ++i)
    {
        data[i] = new char[3];
    }
    std::size_t received;
    if (socket.receive(data, 9, received) != sf::Socket::Done)
    {
        std::cout << "error" << std::endl;
    }
    return data;
}

This is the main class that starts the server and sends data:

#include "ServerGame.hpp"
#include "ClientGame.hpp"
int main()
{
    ServerGame sg;
    sg.connect();
    char arr[3][3] =
    {
        {'a', 'b', 'c'},
        {'d', 'e', 'f'},
        {'g', 'h', 'i'}
    };
    sg.sendData(arr);

}

This is the main class that starts the client and receives data:

#include "ClientGame.hpp"

int main()
{
    ClientGame cg;
    cg.connect();
    char** arr = cg.receiveData();
    std:: cout << arr[0][0] << std::endl;
}

I first compile and run the server class, and then do the same with the client class. When I try to access the first element of the character array I send, it gives a segmentation fault. Why is this happening?


Solution

  • One issue is assuming that char** is the same as a two dimensional array of char. They are not the same.

    When you do this:

    char **data;
    data = new char*[3];
    for(int i = 0; i < 3; ++i)
    {
        data[i] = new char[3];
    }
    

    you are not creating a two-dimensional char array dynamically with a contiguous data layout, similar to a char[3][3]. The memory layout could look something like this:

    data[0] ->  0x32181000
    data[1] ->  0x321A9760
    data[2] ->  0x321AC980
    

    In other words, the rows are strewn all over the heap memory, and are not contiguous.

    Thus when you call this function:

    if (socket.receive(data, 9, received) != sf::Socket::Done)
    

    the socket.receive function is expecting a contiguous buffer of 9 bytes, but you are not providing this. The receive function more than likely overwrites the first row with up to 6 bytes falling off the edge of the first row (the first row is allocated to only have 3 bytes), and thus spilling over into memory that causes the segmentation fault.


    I think the issue stems from you trying to pass a two-dimensional array around from function to function, and making mistakes by using char** in lieu of an actual 2 dimensional array.

    To make things easier without having to introduce pointers, create a struct that contains a 2-dimensional char array, and then pass this struct between the functions.

    struct char2D
    {
        char myArray[3][3];
    };
    

    Then instead of char **, just use an instance of char2D, and refer to the inner array when you need to.

    For example:

    char2D ClientGame::receiveData()
    {
        char2D data;
        std::size_t received;
        if (socket.receive(&data.myArray[0][0], 9, received) != sf::Socket::Done)
        {
            std::cout << "error" << std::endl;
        }
        return data;
    }
    

    Then in main:

    int main()
    {
        ClientGame cg;
        cg.connect();
        char2D arr = cg.receiveData();
        std:: cout << arr.myArray[0][0] << std::endl;
    }
    

    The reason why this works is that a struct is copyable and assignable. So the inner array gets copied whenever you pass it by value and return it by value.