實習項目需要用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, (const char *)&nRecvBuf,sizeof(nRecvBuf));int setsockopt(int sockfd, int level, int optname, const void *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丟棄數據報。
UNIX網絡編程卷1:套接字聯網API(第3版) 中文高清帶完整書簽 PDF 下載見 http://www.linuxidc.com/Linux/2014-04/100222.htm