从 C++ UDP 缓冲区接收字符串
Posted
技术标签:
【中文标题】从 C++ UDP 缓冲区接收字符串【英文标题】:Receiving string from c++ UDP buffer 【发布时间】:2021-12-14 02:22:43 【问题描述】:我正在尝试从 UDP 缓冲区检索结构内的字符串。 这里的想法是首先 memcpy 标头并使用 header.dataSize 告诉 recv 字符串有多大,然后从缓冲区 memcpy 到一个新的字符串变量。该字符串是序列化的数据,然后将被反序列化为结构。
我遇到的问题是,当接收端尝试 memcpy 字符串时,它给了我一个无效指针的错误。
错误:
Data:
free(): invalid pointer
Aborted (core dumped)
我使用 memcpy 错了吗?或者有没有更好的方法来复制指定大小的数据?我尝试使用 0 作为终止字符的 memccpy,但这也不起作用。
发送代码:
// interface details
std::string destIp = "127.0.0.1";
static uint16_t listPort = 10'000;
static uint16_t destPort = 10'001;
// -----------------------------------------------
// Main
// -----------------------------------------------
int main()
// initialize interface
UDP* udp = new UDP(listPort, destIp, destPort);
udp->init();
// create data struct
SerialData data1;
fillSerialData(data1);
// create the out stream
std::ostringstream outStream;
// serialize
cereal::BinaryOutputArchive archive_out(outStream);
archive_out(data1);
// create message out struct
SerialMessage send1;
send1.hdr.domainId = 6;
send1.hdr.msgId = 1;
// copy archive into data location in string format
send1.data = outStream.str();
send1.hdr.dataSize = sizeof(send1.data);
send1.hdr.timeStamp = getTimeStamp();
// send the data
int nbytes = udp->send(reinterpret_cast<uint8_t *>(&send1), sizeof(send1));
// output to console.
std::cout << "\n\tSerialized Data:\n\t" << send1.data << std::endl << std::endl;
std::cout << "\tbytes sent: " << nbytes << "\n\tdataSize: " << send1.hdr.dataSize << "\n\ttimeStamp: " << send1.hdr.timeStamp << "\n\n";
return 0;
接收代码:
int main(int, char **)
std::cout << "Hello, recv!\n";
// initialize signal handler
signal(SIGINT, signalHandler);
// initialize udp interface
UDP *udp = new UDP(listPort, destIp, destPort);
udp->init();
// create buffer to read data into
int recvSize = 0;
int bufSize = 65536;
uint8_t *buffer = new uint8_t[bufSize];
memset(buffer, 0, bufSize);
// loop and recv data
while (!killSignal)
// receive message
if ((recvSize = udp->recv(buffer, bufSize)) < 0)
if (errno == EAGAIN)std::cout << "\n\treceive timeout";
elsestd::cout << "\n\tERROR: " << strerror(errno) << "\n\n";
else
std::cout << "\n\tReceived Message, size: " << recvSize << '\n';
// get incoming message info via header
Header inHdr;
memcpy(&inHdr, buffer, sizeof(Header));
std::string serData;
memcpy(&serData, buffer, sizeof(inHdr.dataSize));
std::cout << "\tdID: " << (int)inHdr.domainId << "\n\tmID: " << (int)inHdr.msgId << "\n\tdataLength: " << inHdr.dataSize << "\n\ttimeStamp: " << inHdr.timeStamp << std::endl;
std::cout << "\nData:\n\t" << serData << std::endl;
// TODO - remove comment tabs below after serData is showing to be filled with the data from buffer.
// deserialization part is good to go.
/*
// create in stream
std::istringstream inStream(sMsg.data);
// create object to store data in.
SerialData data;
// De-serialize
cereal::BinaryInputArchive archive_in(inStream);
archive_in(data);
std::cout << "Data Retreived From Archive:\n" << std::endl;
printSerializedMessageData(data);
*/
// close interface
udp->close();
// clear memory
delete[] buffer;
return 0;
我的结构:
struct Header
uint8_t domainId;
uint8_t msgId;
int msgCnt;
uint16_t dataSize;
uint64_t timeStamp;
;
struct Footer
uint32_t temp;
;
struct Target
std::string type;
double x, y, z;
uint64_t timeStamp;
template <class Archive>
void serialize( Archive & ar )
ar( CEREAL_NVP(type), CEREAL_NVP(x), CEREAL_NVP(y), CEREAL_NVP(z), CEREAL_NVP(timeStamp) );
;
struct SerialData
int numTargets;
std::vector<Target> tgt;
template <class Archive>
void serialize( Archive & ar )
ar( CEREAL_NVP(numTargets), CEREAL_NVP(tgt) );
;
struct SerialMessage
Header hdr;
std::string data;
Footer ftr;
;
【问题讨论】:
无关:UDP* udp = new UDP(listPort, destIp, destPort);
似乎已泄露。您可能无需动态分配就可以逃脱并使用UDP udp(listPort, destIp, destPort);
【参考方案1】:
std::string serData;
memcpy(&serData, buffer, sizeof(inHdr.dataSize));
std::string
只是一个普通的类。一个非常典型的std::string
看起来像:
class string
char *buffer;
size_t size;
size_t max_size;
;
这是std::string
或多或少的摘要。实际细节各不相同,但这就是它的本质,用很多话来说。希望这能说明为什么在这些指针和数据上乱涂乱画,实际上是随机的垃圾,不会完成任何有用的事情,并且会导致崩溃。
正确的做法是:
使用std::string
的resize()
方法来调整字符串数据的大小。
使用 C++17,您可以使用其data()
方法获取指向字符串内部缓冲区的指针。使用早期的 C++ 标准获取字符串中第一个字符的地址,&serData[0]
,就可以解决问题。
最后,现在,经过所有这些工作,您实际上有一个有效的缓冲区,可以将您的数据放入memcpy()
。
但更好的方法是忘记memcpy
,并首先使用数据构造std::string
,使用它的构造函数:
std::string serDatabuffer, buffer+inHdr.dataSize;
就是这样。
【讨论】:
嗨,山姆,感谢您的帮助。这确实消除了我的错误。但是我认为 std::string 是用缓冲区的错误内存位置构造的,因为它没有提取适当的数据。这就是我使用 memcpy 的原因。例如:如果我将“hello world”作为结构内的字符串发送 - 未序列化:使用字符串构造函数中的缓冲区位置,我不会得到它。 我只能对我看到的代码做出判断。我还没有看到你做了什么实际的改变,所以我不能对此发表评论,除了说只有一个buffer
,无论它指向的东西是明确的memcpy
-ed,还是被@复制987654336@ 的构造函数,那么只有可能复制的数据块。此外,还不清楚如何确定 memcpy 使用了正确的内存位置,因为它正在将垃圾复制到无法可靠检查的 std::string
上。听起来你在某个地方也有其他问题。以上是关于从 C++ UDP 缓冲区接收字符串的主要内容,如果未能解决你的问题,请参考以下文章
从 Direct Input 和 GetDeviceState() (C++) 接收键状态
通过 UDP 的 C++ 类在 C# 中使用,都有哪些选项?