Буферы протокола Google не работают с UDP?

Я знаю, что это может быть глупо, но мой пакет сообщений, определенный с помощью буферов протокола Google, не очень хорошо работает с UDP, но отлично работает с TCP. Когда я отправляю обычную строку из сериализованного пакета (в котором у меня есть только простые поля) с клиента на сервер через UDP, все в порядке. Но когда я добавляю повторяющееся поле, сериализованная строка может быть получена только как часть целого. Первое поле будет получено полностью, а все остальные будут потеряны. Код написан на c++, буферы протокола Google 2.3.0, Linux. Любая помощь приветствуется. Спасибо.

Мой прото файл ниже:

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

Сторона сервера:

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

Сторона клиента:

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

Для проблемы, о которой упомянул Джон, я тоже пробовал это, но все равно не работает.

string Sentence = package.SerializeAsString();
n=sendto(clientSocket, Sentence.c_str(), (Sentence.size())+1,0 ,(struct sockaddr *) &sad, sizeof(struct sockaddr));

person Tony    schedule 05.12.2011    source источник
comment
Почему вы не используете TCP? В чем причина использования UDP? (Большая часть времени в любом случае тратится на передачу, поэтому вам следует использовать TCP).   -  person Basile Starynkevitch    schedule 05.12.2011
comment
Вы уверены, что ваши сообщения полностью помещаются в 128 байт?   -  person sarnold    schedule 05.12.2011
comment
Есть много веских причин для использования UDP; например, если вы транслировали живое видео или аудио и не хотели, чтобы передача останавливалась и повторно передавали бесполезные устаревшие данные каждый раз, когда пакет отбрасывался... или если вы отправляли многоадресные или широковещательные данные, которые TCP не поддерживает. .   -  person Jeremy Friesner    schedule 05.12.2011
comment
@Basile Starynkevitch: У меня есть реализация с TCP, она работает хорошо, но мне нужна максимальная производительность, на которую я способен, иначе у меня не будет этой проблемы.   -  person Tony    schedule 05.12.2011
comment
@sarnold, это опечатка, и я только что изменил на 1000, проблема все еще существует, спасибо.   -  person Tony    schedule 05.12.2011


Ответы (1)


Подозреваю, что проблема в этом:

strcpy(Sentence, array);

Вы используете strcpy - он остановится, как только достигнет 0 байта, потому что он обрабатывает эти несколько произвольные двоичные данные как строку. Я подозреваю, что вместо этого вы должны использовать memcpy.

Точно так же не используйте strlen позже. Избегайте всех функций, которые обрабатывают данные как текст.

(В общем, я бы с осторожностью относился к использованию буферов протокола с UDP, если у вас нет веских оснований полагать, что каждое сообщение поместится в один пакет, но это отдельный вопрос.)

person Jon Skeet    schedule 05.12.2011
comment
Спасибо, Джон, но я не думаю, что это проблема, я попробовал код (следовал исходному сообщению), но все еще имеет ту же проблему. - person Tony; 05.12.2011
comment
@Tony: Ну, вы должны в основном разделить свою проблему на две части: сериализация/десериализация буферов протокола и передача пакетов данных без потери чего-либо (включая длину). Вы должны быть в состоянии воспроизвести проблему либо без использования сокетов, или без использования протокольных буферов. В конце концов, пакеты - это просто непрозрачные биты данных - UDP не понимает, что они являются сообщениями буфера протокола. (И независимо от того, была ли это единственная проблема, она определенно была проблемой.) - person Jon Skeet; 05.12.2011
comment
Спасибо за ваш немедленный ответ Джон, я собираюсь попробовать сейчас. - person Tony; 05.12.2011