【问题标题】:How to solve: sending UDP packet using Sendto() got "message too long"如何解决:使用 Sendto() 发送 UDP 数据包得到“消息太长”
【发布时间】:2012-04-08 20:00:13
【问题描述】:

我想使用 sendto() API 通过 UDP 数据包发送视频和音频数据。我使用getsockopt()得到的发送缓冲区大小是114688,但是当数据包小于65536而不是114688时,sendto()返回-1。错误信息是Message too long。

当我使用setsockopt()将发送缓冲区大小调整为200000时,我使用getsockopt()发现发送缓冲区大小不是200000而是262142。所以当我发送一个大小的数据包时仍然出现同样的错误大于 65536。

我对这种情况感到困惑。我想知道是什么原因以及如何解决这个问题。

当我使用 FFMPEG 库发送视频和音频数据包时,没有错误。所以我确信这个问题有解决方案,但我错过了一些东西。

有人可以帮我解决这个问题吗?实在是搞不懂是什么原因。

我使用的操作系统是 ubuntu 11.04,我在 ubuntu 11.10 中得到了相同的结果。

那是我用来创建socket并配置参数的代码:

unsigned char *output_buffer = (unsigned char*)av_malloc(IO_BUFFER_SIZE);
if (NULL == output_buffer) {
    printf("Couldn't allocate input buffer.\n");
    return NULL;
}

output_context_data_t *context_data = (output_context_data_t *)malloc(sizeof(output_context_data_t));
if (NULL == context_data) {
    printf("Could not allocate output context data.\n");
    av_free(output_buffer);
    return NULL;
}

context_data->socket = socket(AF_INET, SOCK_DGRAM, 0);
if(context_data->socket < 0) {
    printf("socket creating fail!\n");
    return NULL;    
}

context_data->socket_addr->sin_family = AF_INET;
context_data->socket_addr->sin_port = htons(output_port);
ret = inet_pton(AF_INET, output_ip, &(context_data->socket_addr->sin_addr));
if(0 == ret) {
    printf("inet_pton fail!\n");
    return NULL;
}

ret = setsockopt(context_data->socket, IPPROTO_IP, IP_MULTICAST_TTL,
                    &option_ttl, sizeof(int));
if(ret < 0) {
    printf("ttl configuration fail!\n");
    return NULL;
}

ret = setsockopt(context_data->socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if(ret < 0) {
    printf("resue configuration fail!\n");
    return NULL;
}   

即发送UDP包的代码:

int send_size = sendto(context_data->socket, buf, buf_size, 0,
                      (struct sockaddr *)context_data->socket_addr, sizeof(*context_data->socket_addr)));
//the video or audio data is in buf and its size is buf_size.

那是我用来获取发送缓冲区大小的代码:

int bufsize; 
int size = sizeof(bufsize);
getsockopt(context_data->socket,SOL_SOCKET, SO_SNDBUF, &bufsize, &size);

那是我用来配置发送缓冲区大小的代码:

tmp = 200000;
ret = setsockopt(context_data->socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp));
if(ret < 0) {
    printf("sending buffer size configuration fail!\n");
    return NULL;
}

【问题讨论】:

    标签: c sockets ubuntu udp


    【解决方案1】:

    您不能使用 UDP 发送大于 2^16 65536 个八位字节的消息(数据报)。 UDP 数据包的长度字段为 16 位。您请求的缓冲区大小与数据包的大小无关,而是操作系统总共缓冲传入和传出的八位位组(分布在多个数据包上)。但是单个数据包不能变大。

    【讨论】:

    • 那么有什么办法可以改变传出的缓冲区大小吗?
    • @user1290120:您可以更改将容纳多个单独的传出数据报的缓冲区的大小。但是没有单个数据报可以大于 65536 个八位字节(字节),这是 UDP 协议本身的限制,没有办法解决这个问题。但无论如何,这么大的数据报可能会被丢弃。数据包不得大于整个路由上的最小 MTU。如果您在以太网链路上,则此 MTU 不可避免地
    • @user1290120:所以你可以改变的是,有多少个单独的数据报可以被“搁置”,而不是单个数据报的最大大小。
    【解决方案2】:

    根据@datenwolf 的回答,您根本不能在单个 UDP 数据报中发送超过 64k 的数据,因为该限制隐含在协议中的两字节长度字段中。

    此外,一次发送这么多也不是一个好主意。您应该将数据包限制在两端之间的路径上的 MTU(通常在 1500 字节或更少的区域内),以免在 IP 层出现碎片

    碎片化不好 - 好吗?

    【讨论】:

      【解决方案3】:

      为什么不直接调用sendto 几次,并在缓冲区中添加一个偏移量?

      int sendto_bigbuffer(int sock, const void *buffer, const size_t buflen, int flags,
                           const struct sockaddr *dest_addr, socklen_t addrlen)
      {
          size_t sendlen = MIN(buflen, 1024);
          size_t remlen  = buflen;
          const void *curpos = buffer;
      
          while (remlen > 0)
          {
              ssize_t len = sendto(sock, curpos, sendlen, flags, dest_addr, addrlen);
              if (len == -1)
                  return -1;
      
              curpos += len;
              remlen -= len;
              sendlen = MIN(remlen, 1024);
          }
      
          return buflen;
      }
      

      类似上述函数的东西会一次发送 1024 个字节的缓冲区。

      【讨论】:

      • 对于 UDP,我强烈反对这一点,因为 UDP 数据包可能以任何顺序到达。您需要为每个数据包添加一些额外的元数据。最好也是 HMAC。
      • @datenwolf 当然,我只是快速地扔了一些东西。甚至没有测试过。 :)
      • sendmsg() 就是为了这个
      猜你喜欢
      • 1970-01-01
      • 2017-04-20
      • 1970-01-01
      • 2021-03-21
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 2012-10-24
      相关资源
      最近更新 更多