1. 程式人生 > >UDP接收多路視訊資料, 接收快取不足了

UDP接收多路視訊資料, 接收快取不足了

(1)困擾幾天的udp內網傳輸部分終於做通了,解決的關鍵就在於setsockopt的呼叫,設定接收緩衝。

遇到的問題是這樣的,主機端傳送udp資料包:

    應用層的包大小為1452byte大小,這樣拆包是根據乙太網的MTU為1500位元組而考慮的(當然外網狀態下並不一定就是乙太網網路,路由MTU可能更加小),因為在網路層和傳輸層還有8byte的udp包頭和20byte的ip包頭,所以乙太網幀大小為1452+8+20 = 1480byte。

   主機端(linux)現在接了11路視訊資料,傳送的資料量還是很大的,但經過測試,資料是可以傳送出去的,傳送端沒有問題。我在客戶端用一個執行緒專門接包,然後進行處理,可總是處理不過來,當連線路數比較多的時候,即碼流增大時,出現接收不過來的情況。開始以為是主機端問題,進行寫檔案測試發現主機端完全可以承受11路資料的傳送(udp資料包),而接收端對每個部分都進行了詳細測試,都沒有效率問題而影響接包的處理,最後將目光放在了接收緩衝的問題上,經過查證,windows程式預設的udp socket的接收和傳送緩衝都是8kB, 而將接收緩衝調大後,馬上解決了丟包現象: 

int n = 512*1024;  setsockopt(m_hRcvSock, SOL_SOCKET, SO_RCVBUF, (const char*)&n, sizeof(n));

    可見對於一般而言,8kB是足夠了,但是對於要接收大量資料時,預設的接收緩衝(udp)是不夠的。需要進行手工設定,否則會造成包的丟失從而資料錯誤。之所以一開始沒有想到這上面是因為我們原來的網路是用tcp進行的視訊傳輸(暫時沒有對tcp的接收和傳送緩衝進行查證),而tcp狀態下我們可以很好的在內網傳輸16路的實時視訊資料,故以為在傳送和接收上udp和tcp一樣不存在瓶頸問題。後查得tcp是會進行流量控制的,下面是一段摘抄:

說到流量控制,不得不提到TCP的另一個重要概念-—視窗。視窗表示了接收主機能接收的最大資料量,並且,視窗大小是隨著主機資源和主機當前正在接收多少個傳輸數量而變化的。主機將視窗欄位用於流量控制,也就是說,流量控制是TCP視窗的一個功能。TCP採用流量控制管理進入接收主機緩衝區的資料流量。如果傳送主機傳輸資料的速度比接收主機處理資料的速度更快以至接收主機緩衝區已滿不能處理更多的資料時,則接收主機就會請求傳送主機降低資料傳送速度直到接收主機可以接收更多的資料為止;相反,如果接收主機能夠處理更多的資料,則會請求傳送主機加快資料的傳送速度,這就是流量控制的用途,它保證了資料在傳輸的過程中完整的傳送到接收主機。

    可以看出由於tcp是基於連線的,所以其在傳輸過程中會犧牲很多來進行傳輸的保證,故即使速度下降也會保證接收端的有序和正確接收。而udp是非連線的,其傳送後不進行任何處理在保證資料的傳輸,高效但無保障,一切檢驗和有序以及完整處理均需要應用層來完成(這讓人想起了RTCP)。

(2)還有一個setsockopt的選相是SO_REUSEADDR, 今天在繫結一個地址來進行偵聽的時候,處理上是每來一個連線,便偵聽其地址傳送來的udp埠,由於要多次繫結,而開始時總遇到地址重複的錯誤,後來一查發現,同一個地址進行埠繫結,好像有一個時間間隔限制,該限制以內不能重複繫結,故我進行了以上的設定,就可以多次重複綁定了~

經過查證,我遇到的是地址使用錯誤的問題:

使用 bind API 函式來繫結一個地址(一個介面和一個埠)到一個套接字端點。可以在伺服器設定中使用這個函式,以便限制可能有連線到來的介面。也可以在客戶端設定中使用這個函式,以便限制應當供出去的連線所使用的介面。bind最常見的用法是關聯埠號和伺服器,並使用萬用字元地址(INADDR_ANY),它允許任何介面為到來的連線所使用。bind 普遍遭遇的問題是試圖繫結一個已經在使用的埠。該陷阱是也許沒有活動的套接字存在,但仍然禁止繫結埠(bind 返回EADDRINUSE),它由 TCP 套接字狀態 TIME_WAIT 引起。該狀態在套接字關閉後約保留 2  4 分鐘。在 TIME_WAIT 狀態退出之後,套接字被刪除,該地址才能被重新繫結而不出問題。

等待 TIME_WAIT 結束可能是令人惱火的一件事,特別是如果您正在開發一個套接字伺服器,就需要停止伺服器來做一些改動,然後重啟。幸運的是,有方法可以避開 TIME_WAIT 狀態。可以給套接字應用 SO_REUSEADDR 套接字選項,以便埠可以馬上重用。

同樣,我在每次UDP偵聽socket使用完畢後,使用closesocket將使用的socket清除,這樣手動釋放後,不需要再進行setsockopt的設定也可以重複綁定了。從此可看出,原本我建立的socket為區域性的,但其釋放好像並不同與普通的c變數的釋放方式,故在函式下次呼叫時候,出現地址重複的錯誤,而手動清除是保險的。該錯誤在linuxwindows平臺均有,但好像windows再呼叫setsockopt,socket接收有異常,偶爾接收不了UDP報文,而linux下沒有此種現象。