2012-03-24 34 views
1

我想使用sendto()API通过UDP数据包发送视频和音频数据。我使用getsockopt()的发送缓冲区大小为114688,但是,当数据包小于65536而不是114688时sendto()返回-1。并且错误消息是消息太长。当我使用setsockopt()调整发送缓冲区大小为200000时,我使用getsockopt(),发现发送缓冲区大小不是200000,而是262142.因此,当我发送数据包的时候,仍然发生了同样的错误尺寸大于65536.如何解决:使用Sendto发送UDP数据包()得到“消息太长”

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

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

有没有人可以帮助我解决这个问题?我真的不明白原因是什么。

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

这是我用来创建套接字和配置参数的代码:

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

回答

5

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

+0

SO有什么办法可以改变传出的缓冲区大小吗? – user1290120 2012-03-24 21:39:13

+0

@ user1290120:您可以更改将保存多个独立传出数据报的缓冲区的大小。但是没有一个数据报可以大于65536个八位字节(字节),这是UDP协议本身的一个限制,而且没有办法解决这个问题。但是大数据报可能会丢弃。一个数据包不能大于整个路由上的最小MTU。如果你在以太网链路上,这个MTU不可避免地<9000,更可能<1500,对于你典型的DSL拨号,它<1490。 – datenwolf 2012-03-24 22:18:23

+2

@ user1290120:所以你可以改变的是,多少个单独的数据报可以被“保留”,但不是单个数据报的最大大小。 – datenwolf 2012-03-24 22:19:11

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

+4

在UDP的情况下,我强烈反对,因为使用UDP数据包可能以任何顺序到达。您需要为每个数据包添加一些额外的元数据。最好也是HMAC。 – datenwolf 2012-03-24 18:06:28

+0

@datenwolf当然,我只是快速地把东西扔在一起。甚至没有测试过它。 :) – 2012-03-24 19:55:00

+1

sendmsg()就是这个 – EdH 2015-09-03 22:39:29

相关问题