Google协议缓冲区不支持UDP?

6
我知道这可能有点傻,但是我的消息包使用Google协议缓冲区定义,在TCP上可以完美地工作,但在UDP上却不能正常工作。 当我从客户端通过UDP发送一个常规字符串(其中只有一些普通字段)到服务器时,一切都好。但是当我添加一个重复的字段时,序列化后的字符串只能接收到整个字符串的一部分。第一个字段将被完全接收,但所有其他字段都将丢失。 代码是用C++编写的,使用了Google协议缓冲区2.3.0版本,运行在Linux上。 欢迎任何帮助。 谢谢。
我的proto文件如下:
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;
}

对于Jon提到的问题,我也尝试过这个方法,但仍然无法解决。

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

2
为什么不使用TCP?使用UDP的原因是什么?(大部分时间都花在传输上,所以应该使用TCP)。 - Basile Starynkevitch
3
你确定你的消息完全在128个字节以内吗? - sarnold
2
使用UDP有很多合理的理由;例如,如果您正在流式传输实时视频或音频,并且不希望传输因数据包丢失而停顿并重新传输无用过时数据... 或者如果您正在发送多播或广播数据,TCP不支持这些功能。 - Jeremy Friesner
@Basile Starynkevitch:我有一个使用TCP的实现,它运行良好,但我需要尽可能达到最佳性能,否则我就会遇到问题。 - Tony
@sarnold,那是个打字错误,我刚刚改成了1000,问题仍然存在,不过还是谢谢你。 - Tony
1个回答

15

我怀疑这就是问题所在:

strcpy(Sentence, array);

你使用了strcpy - 这会在遇到0字节时停止,因为它将这个相当任意的二进制数据视为字符串。我怀疑你应该使用memcpy代替。

同样地,请不要使用后面的strlen函数。避免所有将数据视为文本的函数。

(一般来说,除非你有充分的理由相信每个消息都能适合单个数据包,否则我会对使用UDP协议缓冲区持谨慎态度,但这是另一回事)


谢谢Jon,但我不认为那是问题所在,我尝试了代码(按照原帖),仍然存在同样的问题。 - Tony
2
@Tony:嗯,你基本上应该将问题分成两个部分:序列化/反序列化协议缓冲区和传输数据包而不丢失任何内容(包括长度)。你应该能够在不使用套接字或不使用协议缓冲区的情况下重现问题。毕竟,数据包只是不透明的数据位 - 就像UDP不理解它们是协议缓冲区消息一样。(无论这是否是唯一的问题,它肯定是一个问题。) - Jon Skeet
谢谢您的及时回复,Jon。我现在要尝试一下。 - Tony

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