如何解决:使用Sendto()发送UDP数据包时出现“消息太长”

问题描述 投票:0回答:3

我想使用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
3个回答
10
投票

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


3
投票

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

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


1
投票

为什么不直接调用

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 个字节。

© www.soinside.com 2019 - 2024. All rights reserved.