Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google protocol buffers doesn't work with UDP?

I know it could be silly but my message pack defined with Google protocol buffers is not happily working with UDP while perfectly with TCP. When I send a regular string from serialized package(in which I only have some plain fields) from client to server through UDP, every thing's fine. But when I add a repeated field, the serialized string could only be received a part of the whole. The first field will be received completely, but all the rest will be lost. The code is written in c++, Google protocol buffers 2.3.0, Linux. Any help is welcomed. Thanks.

My proto file is below:

message Package{
    optional string virtualPath = 1;
    optional int32 num = 2;//0=insert, 1=find, 2=remove.
    optional string realFullPath = 3;
    optional bool isDir = 4;
    repeated string listItem = 5;
    optional int32 openMode = 6;
    optional int32 mode = 7;
    optional int32 Operation = 8;       
    optional int32 replicaNo =9; 
}

Server side:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "zht_util.h"
int main(int argc, char *argv[]) {
    struct sockaddr_in sad; 
    int port = 50000; 
    struct sockaddr_in cad; 
    int alen; 
    int serverSocket; 
    char clientSentence[1000];
    char capitalizedSentence[1000];
    char buff[1000];
    int i, n;
    serverSocket = socket(PF_INET, SOCK_DGRAM, 0); /* CREATE SOCKET */
    if (serverSocket < 0) {
        fprintf(stderr, "socket creation failed\n");
        exit(1);
    }

    memset((char *) &sad, 0, sizeof(sad)); 
    sad.sin_family = AF_INET; 
    sad.sin_addr.s_addr = INADDR_ANY; 
    sad.sin_port = htons((u_short) port);

    if (bind(serverSocket, (struct sockaddr *) &sad, sizeof(sad)) < 0) {
        fprintf(stderr, "bind failed\n");
        exit(1);
    }

    while (1) {
        clientSentence[0] = '\0';
        alen = sizeof(struct sockaddr);
        socklen_t len = (socklen_t) alen;
        n = recvfrom(serverSocket, buff, sizeof(buff), 0,
                (struct sockaddr *) &cad, &len);
        strncat(clientSentence, buff, n);
        printf("Server received :%s \n", clientSentence);
    }
    return 0;
}

Client side:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "zht_util.h"
int main(int argc, char    *argv[])

{ 
  struct  sockaddr_in sad; 
  int     clientSocket;     
  struct  hostent  *ptrh;  

  char    *host;           
  int     port;            

  char    Sentence[1000];
  char    modifiedSentence[1000];
  char    buff[1000];
  int     n;

  host = "localhost";
  port = 50000;

 clientSocket = socket(PF_INET, SOCK_DGRAM, 0);
  if (clientSocket < 0) {
    fprintf(stderr, "socket creation failed\n");
    exit(1);
  }

   memset((char *)&sad,0,sizeof(sad)); 
  sad.sin_family = AF_INET;           
  sad.sin_port = htons((u_short)port);
  ptrh = gethostbyname(host); 
  if ( ((char *)ptrh) == NULL ) {
    fprintf(stderr,"invalid host: %s\n", host);
    exit(1);
  }
  memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length);


  HostEntity destination;
    destination.host = "localhost";
    destination.port = 50000;
    int current_sock = -1;

    Package package;
    package.set_virtualpath(randomString(25)); 
    package.add_listitem("item--1");
    package.add_listitem("item--2");
    package.add_listitem("item--3");
    package.add_listitem("item--4");
    package.add_listitem("item--5");
    package.set_realfullpath("Some-Real-longer-longer-and-longer-Paths");
    cout << "package size: " << package.ByteSize() << endl;
    char array[package.ByteSize()];
    package.SerializeToArray(array, package.ByteSize());
    strcpy(Sentence, array);

  n=sendto(clientSocket, Sentence, strlen(Sentence)+1,0 ,
       (struct sockaddr *) &sad, sizeof(struct sockaddr));

  printf(" Client sent %d bytes to the server\n", n);


  close(clientSocket);
  return 0;
}

For the problem that Jon mentioned, I tried this too, still doesn't work.

string Sentence = package.SerializeAsString();
n=sendto(clientSocket, Sentence.c_str(), (Sentence.size())+1,0 ,(struct sockaddr *) &sad, sizeof(struct sockaddr));
like image 583
Tony Avatar asked Dec 05 '11 06:12

Tony


People also ask

Does gRPC support UDP?

Sorry, UDP support in gRPC is not enabled.

Does gRPC use UDP or TCP?

gRPC uses HTTP/2, which multiplexes multiple calls on a single TCP connection.

Is Protobuf backwards compatible?

Protocol buffers provide a language-neutral, platform-neutral, extensible mechanism for serializing structured data in a forward-compatible and backward-compatible way.

Does Protobuf use HTTP?

Protobufs work fine over HTTP in their native binary format.


1 Answers

I suspect this is the problem:

strcpy(Sentence, array);

You're using strcpy - that's going to stop as soon as it hits a 0 byte, because it's treating this somewhat arbitrary binary data as a string. I suspect you should be using memcpy instead.

Likewise, don't use strlen later on. Avoid all functions which treat the data as text.

(In general I'd be wary of using protocol buffers with UDP unless you've got a good reason to believe each message will fit in a single packet, but that's a separate matter.)

like image 154
Jon Skeet Avatar answered Nov 15 '22 06:11

Jon Skeet