achandra03
achandra03

Reputation: 121

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?

Upvotes: 0

Views: 212

Answers (1)

PaulMcKenzie
PaulMcKenzie

Reputation: 35454

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.

Upvotes: 2

Related Questions