C++:我们如何通过 UDP 在结构中发送浮点数据并接收它?
C++: How do we send float data in a structure, over UDP, and receive it?
这是我的发件人代码片段。
if(ThreadQ.try_dequeue(temp)){
if(seqno>=2147483645)
{
seqno=-1;
}
if(frameno>=29)
{
frameno=-1;
}
seqno++;
frameno++;
fragno=0;
std::ofstream f1("packet.txt",std::ios::app);
for(int j=0;j<5;j++)
{
//Packetize-Fragment
fp.fragno=j;
fp.pl.line[0]=temp.line[k++];
fp.pl.line[1]=temp.line[k++];
fp.pl.line[2]=temp.line[k++];
fp.pl.line[3]=temp.line[k++];
fp.seqno = seqno;
fp.frameno = frameno;
retval = send(conn_socket, (char *)&fp, sizeof(fp), 0);
for (i = 0; i < 4; i++)
{
f1 << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch << "\n";
}
}
f1 << "\n\n";
k=0;
}
这些是相关结构,
typedef struct PacketPos{
float x;
float y;
float z;
int ch;
};
typedef struct PacketPL2{
PacketPos line[4];
};
typedef struct FinalPacket{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
};
但是当我在接收方通过 UDP 接收到它时(接收方代码如下所示):
char * Buffer = (char *)malloc(1000);
while (1){
retval = recvfrom(msgsock, Buffer, 10000, 0, (struct sockaddr *)&from, &fromlen);
printf("%d ", retval);
fp = *(FinalPacket*)Buffer;
std::ofstream fout("output.txt", std::ios::app);
for (int i = 0; i < 4; i++)
{
fout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch;
fout << "\n";
}
fout << "\n\n";
}
未收到浮点数据,我只在浮点数据的位置看到 0。我是初学者,所以谁能告诉我我在这里做错了什么?提前致谢。
在您的代码中:fp = *(FinalPacket*)Buffer
不会转换为 Final Packet
,因为 sizeof(FinalPacket)
不是您所期望的。
例如:
假设我们有一个 struct
:
struct Point
{
int x;
int y;
}
那么 sizeof(Point)
不是 2 * sizeof(int)
因为涉及填充。 Google 了解更多信息。
解决方法是使用pragma pack
所以在你的情况下,你应该用 pragma pack 包围你的结构。
示例:
#pragma pack(push, 1)
typedef struct FinalPacket{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
};
#pragma pack(pop)
现在您将可以cast
将buffer
直接struct
。
你的问题很容易解决。我已经为udp通信写了一个简单的测试。现在我给你我的代码,只有关键点:
//my struct:
struct TestCase
{
float x;
float y;
};
//client key points:
TestCase case_2;
case_2.x = 0.5;
case_2.y = 1.0;
if(-1 == sendto(sk_fd, (char*)&case_2, sizeof(case_2), 0, (struct sockaddr*)&remote, sizeof(remote)))
{
cout << "client send data failed, error is " << strerror(errno) << endl;
return 0;
}
//server key points:
TestCase server;
while(1)
{
struct sockaddr_in client;
memset(&server, 0, sizeof(server));
socklen_t client_len = sizeof(client);
const int result = recvfrom(sk_fd, &server, sizeof(server), 0, (struct sockaddr*)&client, &client_len);
if(result < 0)
cout << "server recv error is " << strerror(errno) << endl;
cout << server.x << ' ' << server.y << endl;
break;
}
看完以上这些,我想你应该已经很清楚了。您只需要更改您的代码:char * Buffer = (char *)malloc(1000)
。您应该使用该结构来接收数据。现在你看到了吗?希望对您有所帮助。
我不知道你所在的架构运行。我想它是 x86 或 64 位。
您显示的代码段不完整,至少有一个编码错误。
第一个错误,line 是一个包含 4 个元素的向量:
typedef struct PacketPL2 {
PacketPos line[4];
};
在客户端:
fp.pl.line[0]=temp.line[k++];
k 在某个时刻会大于 3 并且您有缓冲区溢出,因为您在循环外将 k 设置为 0。
我想 conn_socket 已经连接到服务器,对吗?否则又报错
除此之外,您的代码应该可以正常工作。
非常重要:您的代码根本不可移植。如果你想让它可移植,你不能只是将结构转换为缓冲区(反之亦然)。我说的是不同架构之间的可移植性:不同的 int/float/double 大小和不同的字节顺序。
为了使其可移植,您需要为协议定义一些字节顺序、浮点表示和数据大小。然后每次转换一次数据。使用#pragma pack 只会帮助您在结构中进行数据对齐,但与此同时,它不仅依赖于编译器,而且对处理器的效率也较低。
我用你的代码实现了一个 UDP 客户端-服务器(但在客户端使用 sendto),除了上面的错误,它工作正常。代码不是很好,我试着把你的代码片段放在里面但它有效。
客户:
typedef struct PacketPL2
{
PacketPos line[4];
} s_pp2;
typedef struct FinalPacket
{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
} s_fp;
int main()
{
int seqno = 214000098;
int frameno = 10;
seqno++;
frameno++;
int fragno=0;
s_fp fp;
s_pp2 temp;
int conn_socket;
struct sockaddr_in servaddr;
temp.line[0].x = 4.56;
temp.line[0].z = 3.56;
temp.line[1].x = 7.99;
temp.line[1].z = 5.99;
temp.line[2].x = 3.99;
temp.line[2].z = 4.59;
temp.line[3].x = 1.51;
temp.line[3].z = 2.33;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
servaddr.sin_port=htons(32000);
conn_socket = socket(AF_INET, SOCK_DGRAM, 0);
using namespace std;
int k = 0;
for(int j=0;j<5;j++)
{
//Packetize-Fragment
fp.fragno=j;
//ERROR, buffer overflow: WHEN K > 3
fp.pl.line[0]=temp.line[k++];
fp.pl.line[1]=temp.line[k++];
fp.pl.line[2]=temp.line[k++];
fp.pl.line[3]=temp.line[k++];
fp.seqno = seqno;
fp.frameno = frameno;
// int retval = send(conn_socket, (char *)&fp, sizeof(fp), 0);
int retval = sendto(conn_socket,(char *)&fp, sizeof(fp),0, (struct sockaddr *)&servaddr,sizeof(servaddr));
cout << "RETVAL cli:" << retval << endl;
for (int i = 0; i < 4; i++)
{
cout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch << "\n";
}
}
cout << "\n\n";
//K IS INITIALIZED HERE
k=0;
return 0;
}
服务器:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
typedef struct PacketPos
{
float x;
float y;
float z;
int ch;
} s_pp;
typedef struct PacketPL2
{
PacketPos line[4];
} s_pp2;
typedef struct FinalPacket
{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
} s_fp;
int main()
{
char * Buffer = (char *)malloc(1000);
int msgsock;
s_fp fp;
struct sockaddr_in servaddr, from;
socklen_t fromlen;
bzero(&from, sizeof(from));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(32000);
msgsock = socket(AF_INET, SOCK_DGRAM, 0);
bind(msgsock,(struct sockaddr *)&servaddr,sizeof(servaddr));
using namespace std;
while (1)
{
int retval = recvfrom(msgsock, Buffer, 10000, 0, (struct sockaddr *)&from, &fromlen);
cout << "RETVAL:" << retval << endl;
fp = *(FinalPacket*)Buffer;
for (int i = 0; i < 4; i++)
{
cout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch;
cout << "\n";
}
cout << "\n\n";
}
return 0;
}
请参阅这些链接以了解浮点表示和大小:
What is the size of float and double in C and C++?
Fixed-size floating point types
这是我的发件人代码片段。
if(ThreadQ.try_dequeue(temp)){
if(seqno>=2147483645)
{
seqno=-1;
}
if(frameno>=29)
{
frameno=-1;
}
seqno++;
frameno++;
fragno=0;
std::ofstream f1("packet.txt",std::ios::app);
for(int j=0;j<5;j++)
{
//Packetize-Fragment
fp.fragno=j;
fp.pl.line[0]=temp.line[k++];
fp.pl.line[1]=temp.line[k++];
fp.pl.line[2]=temp.line[k++];
fp.pl.line[3]=temp.line[k++];
fp.seqno = seqno;
fp.frameno = frameno;
retval = send(conn_socket, (char *)&fp, sizeof(fp), 0);
for (i = 0; i < 4; i++)
{
f1 << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch << "\n";
}
}
f1 << "\n\n";
k=0;
}
这些是相关结构,
typedef struct PacketPos{
float x;
float y;
float z;
int ch;
};
typedef struct PacketPL2{
PacketPos line[4];
};
typedef struct FinalPacket{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
};
但是当我在接收方通过 UDP 接收到它时(接收方代码如下所示):
char * Buffer = (char *)malloc(1000);
while (1){
retval = recvfrom(msgsock, Buffer, 10000, 0, (struct sockaddr *)&from, &fromlen);
printf("%d ", retval);
fp = *(FinalPacket*)Buffer;
std::ofstream fout("output.txt", std::ios::app);
for (int i = 0; i < 4; i++)
{
fout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch;
fout << "\n";
}
fout << "\n\n";
}
未收到浮点数据,我只在浮点数据的位置看到 0。我是初学者,所以谁能告诉我我在这里做错了什么?提前致谢。
在您的代码中:fp = *(FinalPacket*)Buffer
不会转换为 Final Packet
,因为 sizeof(FinalPacket)
不是您所期望的。
例如:
假设我们有一个 struct
:
struct Point
{
int x;
int y;
}
那么 sizeof(Point)
不是 2 * sizeof(int)
因为涉及填充。 Google 了解更多信息。
解决方法是使用pragma pack
所以在你的情况下,你应该用 pragma pack 包围你的结构。
示例:
#pragma pack(push, 1)
typedef struct FinalPacket{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
};
#pragma pack(pop)
现在您将可以cast
将buffer
直接struct
。
你的问题很容易解决。我已经为udp通信写了一个简单的测试。现在我给你我的代码,只有关键点:
//my struct:
struct TestCase
{
float x;
float y;
};
//client key points:
TestCase case_2;
case_2.x = 0.5;
case_2.y = 1.0;
if(-1 == sendto(sk_fd, (char*)&case_2, sizeof(case_2), 0, (struct sockaddr*)&remote, sizeof(remote)))
{
cout << "client send data failed, error is " << strerror(errno) << endl;
return 0;
}
//server key points:
TestCase server;
while(1)
{
struct sockaddr_in client;
memset(&server, 0, sizeof(server));
socklen_t client_len = sizeof(client);
const int result = recvfrom(sk_fd, &server, sizeof(server), 0, (struct sockaddr*)&client, &client_len);
if(result < 0)
cout << "server recv error is " << strerror(errno) << endl;
cout << server.x << ' ' << server.y << endl;
break;
}
看完以上这些,我想你应该已经很清楚了。您只需要更改您的代码:char * Buffer = (char *)malloc(1000)
。您应该使用该结构来接收数据。现在你看到了吗?希望对您有所帮助。
我不知道你所在的架构运行。我想它是 x86 或 64 位。 您显示的代码段不完整,至少有一个编码错误。 第一个错误,line 是一个包含 4 个元素的向量:
typedef struct PacketPL2 {
PacketPos line[4];
};
在客户端:
fp.pl.line[0]=temp.line[k++];
k 在某个时刻会大于 3 并且您有缓冲区溢出,因为您在循环外将 k 设置为 0。
我想 conn_socket 已经连接到服务器,对吗?否则又报错
除此之外,您的代码应该可以正常工作。
非常重要:您的代码根本不可移植。如果你想让它可移植,你不能只是将结构转换为缓冲区(反之亦然)。我说的是不同架构之间的可移植性:不同的 int/float/double 大小和不同的字节顺序。
为了使其可移植,您需要为协议定义一些字节顺序、浮点表示和数据大小。然后每次转换一次数据。使用#pragma pack 只会帮助您在结构中进行数据对齐,但与此同时,它不仅依赖于编译器,而且对处理器的效率也较低。
我用你的代码实现了一个 UDP 客户端-服务器(但在客户端使用 sendto),除了上面的错误,它工作正常。代码不是很好,我试着把你的代码片段放在里面但它有效。
客户:
typedef struct PacketPL2
{
PacketPos line[4];
} s_pp2;
typedef struct FinalPacket
{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
} s_fp;
int main()
{
int seqno = 214000098;
int frameno = 10;
seqno++;
frameno++;
int fragno=0;
s_fp fp;
s_pp2 temp;
int conn_socket;
struct sockaddr_in servaddr;
temp.line[0].x = 4.56;
temp.line[0].z = 3.56;
temp.line[1].x = 7.99;
temp.line[1].z = 5.99;
temp.line[2].x = 3.99;
temp.line[2].z = 4.59;
temp.line[3].x = 1.51;
temp.line[3].z = 2.33;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
servaddr.sin_port=htons(32000);
conn_socket = socket(AF_INET, SOCK_DGRAM, 0);
using namespace std;
int k = 0;
for(int j=0;j<5;j++)
{
//Packetize-Fragment
fp.fragno=j;
//ERROR, buffer overflow: WHEN K > 3
fp.pl.line[0]=temp.line[k++];
fp.pl.line[1]=temp.line[k++];
fp.pl.line[2]=temp.line[k++];
fp.pl.line[3]=temp.line[k++];
fp.seqno = seqno;
fp.frameno = frameno;
// int retval = send(conn_socket, (char *)&fp, sizeof(fp), 0);
int retval = sendto(conn_socket,(char *)&fp, sizeof(fp),0, (struct sockaddr *)&servaddr,sizeof(servaddr));
cout << "RETVAL cli:" << retval << endl;
for (int i = 0; i < 4; i++)
{
cout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch << "\n";
}
}
cout << "\n\n";
//K IS INITIALIZED HERE
k=0;
return 0;
}
服务器:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
typedef struct PacketPos
{
float x;
float y;
float z;
int ch;
} s_pp;
typedef struct PacketPL2
{
PacketPos line[4];
} s_pp2;
typedef struct FinalPacket
{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
} s_fp;
int main()
{
char * Buffer = (char *)malloc(1000);
int msgsock;
s_fp fp;
struct sockaddr_in servaddr, from;
socklen_t fromlen;
bzero(&from, sizeof(from));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(32000);
msgsock = socket(AF_INET, SOCK_DGRAM, 0);
bind(msgsock,(struct sockaddr *)&servaddr,sizeof(servaddr));
using namespace std;
while (1)
{
int retval = recvfrom(msgsock, Buffer, 10000, 0, (struct sockaddr *)&from, &fromlen);
cout << "RETVAL:" << retval << endl;
fp = *(FinalPacket*)Buffer;
for (int i = 0; i < 4; i++)
{
cout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch;
cout << "\n";
}
cout << "\n\n";
}
return 0;
}
请参阅这些链接以了解浮点表示和大小:
What is the size of float and double in C and C++?
Fixed-size floating point types