2012-03-24 32 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

相關問題