向服务器发送对象
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 个 int
s 和一个 std::string
这个 int
s 可以很容易地用 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);
}
您好,我有一个客户端程序和一个服务器程序。 我创建了一个这样的结构
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 个 int
s 和一个 std::string
这个 int
s 可以很容易地用 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
现在我们的结构看起来像
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);
}