在 SFML 中使用 TcpSocket 发送数据导致分段错误

Sending data using TcpSocket in SFML causes segmentation fault

我正在使用 SFML 的 TcpSocket class 发送和接收数据,但由于某种原因,我一直收到错误 Segmentation fault: 11。这是服务器 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;
} 

这是客户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;
}

这是启动服务器和发送数据的主要class:

#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);

}

这是启动客户端和接收数据的主要class:

#include "ClientGame.hpp"

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

我首先编译并运行 服务器class,然后对客户端class 执行相同的操作。当我尝试访问我发送的字符数组的第一个元素时,它给出了一个分段错误。为什么会这样?

一个问题是假设 char**char 的二维数组相同。它们相同。

当你这样做时:

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

您没有使用连续数据布局动态创建 two-dimensional char 数组,类似于 char[3][3]。内存布局可能如下所示:

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

换句话说,行散布在整个堆内存中,并且不连续。

因此当你调用这个函数时:

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

socket.receive 函数需要一个 9 字节的连续缓冲区,但您没有提供它。 receive 函数很可能会覆盖第一行,最多 6 个字节从第一行的边缘掉落(第一行被分配为只有 3 个字节),因此溢出到导致分段的内存中错误。


我认为问题源于您试图将 two-dimensional 数组从一个函数传递到另一个函数,并使用 char** 代替实际的二维数组而犯了错误。

为了在不引入指针的情况下简化操作,创建一个包含二维 char 数组的 struct,然后在函数之间传递此结构。

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

那么不用char **,只要使用char2D的一个实例,在需要的时候引用内部数组。

例如:

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;
}

然后在 main:

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

之所以可行,是因为 struct 是可复制和可分配的。因此,每当您按值传递内部数组并按值传递 return 时,都会复制内部数组。