向服务器发送对象

Sending an object to server

您好,我有一个客户端程序和一个服务器程序。 我创建了一个这样的结构

struct player
{
    Int x,y;
    Int score;
    Std::string name;
}

我创建了这个对象:

player p;

然后我初始化了它:

p.x = 100; p.y = 200; p.score = 281;
p.name = "DiCri";

我将对象发送到服务器。

SDLNet_TCP_Send(client, (char*)&p, sizeof(p));

一切正常。 服务器获取带有数据的对象。 唯一的问题是名字。 服务器说 x 是 100,y 是 200,score 是 281,name 是一种奇怪的随机符号。 我不知道为什么。 帮助。如何解决? 如果那是一个 char* 而不是一个字符串,也会发生这种情况。

谢谢

编辑1: 我发现了一个类似于我的问题:Serialization of an object。 提出这个问题的用户也想通过网络发送这个对象。 我会尝试按照答案

编辑2: 使用 char name[100] 它有效。

编辑3: 谢谢,现在一切正常!抱歉英语不好,因为我是意大利人!

首先,Std::string应该换成char数组。 您还知道 Int 在不同 OS 上的网络表示。 htons() 和 ntohs 必须用于通过网络发送 16 位整数。

Player 包含 3 个 ints 和一个 std::string 这个 ints 可以很容易地用 write (watch out for the differing byte orders, endian, used by processors) ,但是 string 是一个太复杂的对象。选项 1 是用固定大小的 char 数组替换 string 以简化结构,但这增加了更多的痛苦而不是它的价值。 char 数组很容易溢出,无论你是否使用它,总是写入数组的完整大小。

更好的方法是建立通信协议并序列化数据。

首先确定协议使用的字节序,让双方都清楚使用的字节顺序。传统上Big Endian用于网络通信,但是big endian设备越来越少,所以这种情况越来越多,"Your call."

让我们坚持使用大字节序,因为这方面的工具是古老的、众所周知的,并且已经建立。如果您使用的是套接字库,则很有可能您可以访问 tools to perform the operations required.

接下来,显式确定整数数据类型的大小。 C++ 的不同实现可能在 cstdint

中定义了 different sizes for fundamental types. Solution for this one is simple: Don't use the fundamental types. Instead, use the fixed width integers

现在我们的结构看起来像

struct player
{
    int x,y;
    int score;
    std::string name;
}

它需要看起来更像

struct player
{
    int32_t x,y;
    int32_t score;
    std::string name;
}

现在整数的大小已经不足为奇了。代码编译或不支持 int32_t。不要笑了。那里仍然有 8 位微控制器。

现在我们知道了整数是什么样子的,我们可以编写一对函数来处理读写:

bool sendInt32(int32_t val)
{
    int32_t temp = htonl(val);
    int result = SDLNet_TCP_Send(client, (char*)&temp, sizeof(temp));
    return result == sizeof(temp); // this is simplified. Could do a lot more here
}

bool readInt32(int32_t & val)
{
    int32_t temp;

    if (readuntil(client, (char*)&temp, sizeof(temp)))
    {
        val = ntohl(temp);
        return true;
    }
    return false;
}

其中 readuntil 是一个函数,它一直从套接字读取数据,直到套接字失败或读取了所有请求的数据。不要假设您想要的所有数据都会同时到达。有时你必须等待。即使你把它全部写成一个块,它也不一定全部到达一个块。处理这个问题是另一个问题,但它几乎每周都会被问到,所以你应该能够通过一些搜索找到一个好的答案。

现在 string。你走 C 路线并发送一个空终止字符串,但我发现这使得 reader 比它需要的要复杂得多。相反,我在字符串数据前加上长度,所以 reader 所要做的就是读取长度,然后读取长度字节。

基本上是这样的:

bool sendstring(const std::string & str)
{
    if (sendInt32(str.size()))
    {
        int result = SDLNet_TCP_Send(client, str.c_str(), str.size());
        return result == str.size(); 
    }
    return false;
}

bool readstring(std::string & str)
{
    int32 len;
    if (readInt32(len))
    {
        str.resize(len);
        return readuntil(client, str.data(), len) == len;
    }
    return false;
}

总的来说,一个作家看起来像

bool writePlayer(const Player & p)
{
    return sendInt32(p.x) && 
           sendInt32(p.y) && 
           sendInt32(p.score) &&
           sendString(p.name);
}

和一个reader

bool readPlayer(Player & p)
{
    return readInt32(p.x) && 
           readInt32(p.y) && 
           readInt32(p.score) &&
           readString(p.name);
}