谷歌协议缓冲区(Java 到 C++)

3

我正试图在Windows上使用Java和Raspberry Pi上的C++之间建立一个TCP/IP套接字连接。传输的消息是一个Google协议缓冲区消息,proto设置如下:

package package_name;

message Win2Pi{
    optional int32 num1= 1;
    optional int32 num2= 2;
    optional int32 num3= 3;
    optional int32 num4= 4;
    optional bool logic1= 5;
    optional bool logic2= 6;
    optional bool logic3= 7;
    optional bool logic4= 8;
    optional bool logic5= 9;
    optional int32 num5= 10;
    optional bool logic6= 11;
}

我有以下Java代码(作为客户端):
/* Java Code to Open Socket, Create Protobuf Message, and Send */
Socket clientSocket = new Socket(hostName, portNumber);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
WinToPi.Builder w2p = WinToPi.newBuilder();
w2p.setNum1(255);
w2p.setNum2(255);
w2p.setNum3(255);
w2p.setNum4(255);
w2p.setLogic1(true);
w2p.setLogic2(true);
w2p.setLogic3(true);
w2p.setLogic4(true);
w2p.setLogic5(false);
w2p.setNum5(7);
w2p.setLogic6(true);
w2p.build().writeTo(clientSocket.getOutputStream());

我有以下的C++代码(作为服务器):
//Protobuf Setup Variables
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[1024];
struct sockaddr_in serv_addr, cli_addr;
int n;

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0){ 
    std::cout << "Error Opening Socket!" << std::endl;
    exit(1); //error
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0){ 
    std::cout << "Error on Binding!" << std::endl; ;
    exit(1); //error
}

listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
    std::cout << "ERROR on accept" << std::endl;
    exit(1);
}

/* Clear Buffer and Read Message from Client */
bzero(buffer,1024);
n = read(newsockfd,buffer,1023);

std::cout << "n: " << n << std::endl;

if (n < 0){ 
    std::cout << "Error Reading From Socket!" << std::endl;
}

/* Translate Shoreside to Pi Message */
std::string inputStr = std::string(buffer);
package_name::WinToPi w2p;
w2p.ParseFromString(inputStr);

使用静态消息时,我能够接收到适当数量的字节和值。因此,我开始让Java端动态更改这些值。然而,在C++端,我会收到正确数量的字节,但大多数变量的值没有改变(只有前几个)。在Java端检查打包和传输的Google协议缓冲区消息时,似乎我发送了正确的值。那么,在C++中接收Google协议缓冲区消息是否有更好的方法?
1个回答

0

你最大的问题似乎是这个:

std::string inputStr = std::string(buffer);

当您使用const char*构造一个字符串时,它会寻找第一个NUL作为终止符。您应该改用...
std::string inputStr = std::string(buffer, n);

...这将确保整个接收到的数据块被存储到字符串中。

另一个问题:

  • 在套接字上执行read可能会返回多次发送的任何内容,因此您应该始终采用一种约定来确定何时停止读取(例如,客户端和发送方都知道的固定字节数-可能来自结构大小、哨兵字符或序列,如换行符或NUL,或固定大小的长度前缀)

    • 这是流缓冲的自然结果:假设您的客户端应用程序调用write/send三次,而操作系统太忙以至于实际上无法将任何数据放入网络数据包中:当它确实这样做时,它可以将第一个和第二个“写”中的一半放入一个数据包中,然后将第二个的其余部分与第三个一起发送到另一个数据包中;如果接收方期望每个read读取多字节逻辑消息的开头,他们将感到震惊

至于更好的方法,对于一些小型休闲使用,我发现boost::asio非常简洁、清晰,并且使用起来很愉快...有很多在线文档、示例和教程。


感谢您的及时回复!将n添加到char转换为字符串中对我非常有帮助。目前,我不太担心消息会被分开,因为它们通常最多不超过40个字节。我一定会记住您的建议并继续前进! - mariemjm25

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接