简体   繁体   中英

Google Protocol Buffer (Java to C++)

I am attempting to setup a TCP/IP socket connection between Java and C++, with Java on Windows and C++ on a Raspberry Pi. The message being transmitted is a Google Protocol Buffer message, with a proto set as below:

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;
}

I have the following code for Java (which acts as a client):

/* 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());

I have the following code for C++ (which acts as a server):

//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);

With a static message, I am able to receive the appropriate number of bytes and values. With this, I moved to having values change dynamically on the Java side. However, it appears that on the c++ side, I will receive the correct number of bytes but the values will not have changed for a majority of the variables (only the first few). When I check the packaged and transmitted Google Protocol Buffer message on the Java side, it appears that I am sending the correct values. Is there a better approach for receiving a Google Protocol Buffer message in c++?

Your biggest problem seems to be this:

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

When you construct a string like that - from a const char* - it will look for the first NUL as the terminator. You should instead use...

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

...which will ensure the entire chunk of received data is stored into the string.

Another problem:

  • read on a socket may return whatever's sent over multiple calls, so you should always adopt a convention for working out when to stop reading (eg a fixed number of bytes known to client and sender - perhaps from a structure size, or a sentinel character or sequence such as newline or NUL, or a fixed-sized length prefix)

    • this is a natural consequence of buffering over the stream: say your client app calls write / send thrice while the OS is too busy to actually get any of the data into a network packet: when it does do so it may be able to fit the first and half the second "write" into one packet, then send the rest of the second along with the third in another; if the receiver expects each read to read the start of a multi-byte logical message, they're in for a shock

As for better approaches, for some small casual use I've found boost::asio makes for very concise, clear code and is a pleasure to use... lots of docs, examples, tutorials online.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM