UDP接收端緩衝區和丟包問題
轉自:http://blog.csdn.net/ljh0302/article/details/49738191
實習專案需要用Winsock核心模式驅動提供的sockets方法,,這個驅動負責連線和緩衝管理,對應用程式提供socket風格的程式設計介面。
大概的流程是傳送端將一幅影象分成多個包進行傳送,接收端接收包整合成影象並顯示。
通過測試發現,傳送端是相機,採用udp協議,傳送的影象資料包有3428個,並且通過wireshark抓取到全部的包,但是接收端只能接收到3000個包左右,出現了丟包現象。
分析UDP丟包的原因:
1)呼叫recvfrom方法接收到資料後,處理資料花費時間太長,再次呼叫recvfrom
通過將接收到資料存入一個緩衝區,並迅速返回繼續recvfrom,排除了這個原因
2)通過資料包的id域觀察發現,接收的情況:接收到一些包之後丟失一些包,重複如此。分析原因,可能是因為連續多個UDP包超過了udp的接收端緩衝區(UDP包過大或者包頻率過快),導致丟包。於是將接收緩衝設定為5M,問題就解決了。
- int nRecvBuf = 5 * 1024 * 1024; //設定成5M
-
Setsockopt(s,SOL_SOCKET, SO_RCVBUF, (constchar *)&nRecvBuf,sizeof(nRecvBuf));
- int setsockopt(int sockfd, int level, int optname, constvoid *optval, socklen_t optlen);
- sockfd: 標識一個套接字的描述字
- level: 選項定義的層次:支援SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP,和IPPROTO_IPV6
- optname:需設定得選項 SO_RCVBUF(接收緩衝區),SO_SNDBUF(傳送緩衝區)
- optval:指標,指向存放選項待設定的新值的緩衝區
- optlen:optval的大小
下面的內容來自:
概念:
MTU :鏈路層上資料幀中資料的最大值,即IP資料報的整個值。資料進入協議棧的封裝過程。
MSS:TCP報文段中資料的最大值----MSS選項只能出現在SYN報文中。
TCP輸出:
每個TCP套介面都有一個傳送緩衝區,我們可以用SO_SNDBUF套介面選項來改變這個緩衝區的大小。當應用程式呼叫write時,核心從應用程序的緩衝區中拷貝所有資料到套介面的傳送緩衝區。如果套介面傳送緩衝區容不下應用程式所有的程式(或者應用程式的緩衝區大於套介面傳送緩衝區,或者是套介面傳送緩衝區還有其他資料),應用程序將被掛起,這裡假設write是阻塞的。核心將不從write系統呼叫返回,直到將應用程序緩衝區的所有資料都拷貝到套介面傳送緩衝區。因此從寫一個TCP套介面的write呼叫成功返回僅僅代表我們重新使用應用程序的緩衝區。它並不告訴我們對端TCP或者應用程序已經接收到資料。
UDP輸出:
這一次我們展示的套介面傳送緩衝區用虛框表示,因為它並不存在。UDP套介面有傳送緩衝區大小(SO_SNDBUF修改),不過它僅僅是寫到套介面的UDP資料報的大小上限。如果一個應用程式寫一個大於套介面傳送緩衝區大小的資料報,核心將返回EMSGSIZE錯誤。既然UDP不可靠,它不必儲存應用程式的資料拷貝,因此無需真正的傳送緩衝區(應用程序的資料在沿協議棧往下傳遞,以某種形式拷貝到核心緩衝區,然而資料鏈路層在送出資料之後將丟棄該拷貝)。
根據上圖可以發現,UDP沒有MSS的概念,如果某個UDP應用程序傳送大資料,那麼它比TCP應用程式更容易分片。從UDP套介面write成功返回僅僅表示使用者寫入的資料報或者所有片段已經加入到資料鏈路層的輸出佇列。如果該佇列沒有足夠的空間存放該資料包或者它的某個片段,核心通常返回給應用程序一個ENOBUFS錯誤。
TCP和UDP都擁有套介面接收緩衝區。TCP套介面接收緩衝區不可能溢位,因為TCP具有流量控制,然而對於TCP來說,當接收到的資料報裝不進套介面接收緩衝區時,該資料報就丟棄。UDP是沒有流量控制的:較快的傳送端可以很容易淹沒較慢的接收端,導致接收端的UDP丟棄資料報。