Linux網路程式設計-UDP接收資料丟包解決方案
序言
專案涉及基於UDP的socket通訊,該部分的基本情況如下:
- 發端程式:主函式開啟4個發包執行緒,每個執行緒傳送一定量的資料,通過限制發包速率限制發包流量。
- 收端程式:主函式對應開啟4個收包執行緒,每個執行緒收取對應埠的資料,收到資料包即時封裝處理。
其他說明:
本地收發。如果不限制發包速率將會非常快
基於UDP。使用recvfrom()函式收包
recvfrom()接收後立即將包加入佇列並封裝處理,即一次處理單個包
發包流量最大160Mbps = 20MBps
多種流量:160Mbps,80Mbps,40Mbps等進行測試
- 測試方式:
- 傳送一定時間的資料。
- 如設定傳送1s,傳送資料量160Mbits,即160Mbps流量
- 收到資料包後進行包計數。
- 統計收包率/丟包率
- 記錄收端處理資料耗時,對比發端耗時。
- 即測試收端處理速度,不過應該注意此處的耗時只是處理所收到資料包的耗時,並不一定是所有資料量的耗時,如果存在丟包的話。
- 傳送一定時間的資料。
但是現在測試出現的問題如下:
收包率低/丟包率高。
- 丟包率最高時可達67%
- 速率逐漸降低,收包率有上升趨勢
- 到一定速率,速率繼續降低,收包數也不繼續上升
- 丟包率最高時可達67%
最大容量下耗時超過了發包耗時
- 耗時最大可達傳送時間的2倍以上
- 速率逐漸降低,耗時有減小趨勢
- 到一定速率,速率繼續降低,由於只能處理一定收包數,耗時基本穩定
- 耗時最大可達傳送時間的2倍以上
1. 問題分析
收包率低/丟包率高的原因分析
(1) 快取太小,不能及時接收資料。
- 連續多個UDP包超過了UDP接收緩衝區大小
- 如:UDP包過大
- 如:UDP發包速率過快,突發大資料流量超過了緩衝區上限
- 連續多個UDP包超過了UDP接收緩衝區大小
(2)recvfrom()接收到資料之後處理速度太慢
- 如果資料接收和處理/渲染是連續進行的,那麼可能由於資料處理過慢,兩次recvfrom呼叫的時間間隔裡發過來的包丟失
2. 問題驗證和解決措施
(1) 快取太小不能及時接收資料
[1] 分析:
UDP無需真正的傳送緩衝區:
- UDP是不可靠連線,不必儲存應用程序的資料拷貝,因此無需真正的傳送緩衝區(TCP需要)。應用程序的資料在沿協議棧往下傳遞,以某種形式拷貝到核心緩衝區,然而資料鏈路層在送出資料之後將丟棄該拷貝
UDP是沒有流量控制的:
- 較快的傳送端可以很容易淹沒較慢的接收端,導致接收端的UDP丟棄資料報。
- UDP套接字的緩衝區是以一個個報文為單位進行排隊的,呼叫一次recvfrom表示提取一個報文,和TCP基於位元組流的方式是不同的
因此,如果socket接收快取設定過小,就會因為UDP包過大或者發包速率過快而丟包
[2] 解決方法:重新設定UDP接收緩衝區大小
- UDP接收緩衝區預設值:cat /proc/sys/net/core/rmem_default
- 本系統:212992 = 208K
- UDP接收緩衝區最大值:cat /proc/sys/net/core/rmem_max,UDP最大可設定值的一半
- 本系統:212992 = 208K,即最大值425984 = 416K
UDP接收緩衝區最小值:sysctl -a | grep rmem
- 本系統:net.ipv4.udp_rmem_min = 4096 = 2K,由核心的巨集決定
UDP傳送緩衝區預設值:cat /proc/sys/net/core/wmem_default
- 本系統:212992 = 208K
- UDP傳送緩衝區最大值:cat /proc/sys/net/core/wmem_max
- 本系統:212992 = 208K,即最大值425984 = 416K
- UDP傳送緩衝區最小值:sysctl -a | grep wmem
- 本系統:net.ipv4.udp_wmem_min = 4096 = 2K,由核心的巨集決定
- 本系統:net.ipv4.udp_wmem_min = 4096 = 2K,由核心的巨集決定
- UDP接收緩衝區預設值:cat /proc/sys/net/core/rmem_default
[3] 解決步驟:
調整UDP緩衝區大小:使用函式setsockopt()函式修改接收緩衝區大小
- 緩衝區改大可以處理突發的大流量資料,不至於資料(視音訊等)變化、流量突然增大的時候緩衝區溢位
或
重新設定系統緩衝區最大值,再調整UDP緩衝區大小:
開啟配置檔案:sudo vim /etc/sysctl.conf
在檔案末尾新增:net.core.rmem_max = 6291456
- 將接收緩衝最大值設定為12582912 = 12M
執行配置:sysctl -p
重新檢視最大值:cat /proc/sys/net/core/rmem_max
發現系統接收緩衝最大值已改變,此時可以通過setsockopt函式設定更大接收快取。傳送緩衝最大值也可以通過類似方式修改:net.core.wmem_max = 6291456,sysctl -p
/* setsockopt()函式修改:*/
//函式原型
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的大小
//示例
int recv_size = 2 * 1024 * 1024; //設定為4M
setsockopt(s,SOL_SOCKET, SO_RCVBUF, (const char *)&recv_size,sizeof(recv_size));
(2)recvfrom()接收到資料之後處理速度太慢
[1] 分析:
UDP無需真正的緩衝區,不必儲存應用程序的資料拷貝
- 這就對資料處理的實時性提出了很高要求
recvfrom()接收速率並不是系統受限因素
- recvfrom()處理速度是由CPU速度決定的(看到過這種說法,待驗證),如果執行緒正常呼叫,幾百Mbps的速度該函式肯定能跟上
資料處理是速度受限因素之一
- 程式在recvfrom接收到資料之後還進行了封裝處理,即資料接收和資料處理是繫結的,處理速度跟不上接收速度(渲染速度跟不上socket接收速度)
執行緒掛起再喚醒耗時受限速度之二
- 儘管由CPU決定的recvfrom接收速度足夠快,但如果接收資料執行緒從掛起再到喚醒接收資料,這個過程耗時可達數百毫秒
- 儘管由CPU決定的recvfrom接收速度足夠快,但如果接收資料執行緒從掛起再到喚醒接收資料,這個過程耗時可達數百毫秒
[2] 解決方法:
- 資料處理速度受限:資料接收和資料處理分離
- 接收資料單獨儲存,然後直接返回,保證recvfrom不丟資料或超時
- 資料處理由其他函式或執行緒獨立完成
- 執行緒響應速度受限:保持執行緒一直在執行或監聽狀態
- 因為資料接收和處理函式都在同一執行緒,可將資料接收和處理從邏輯上分開
- 如果不在統一執行緒,由於資料到了再起執行緒速度過慢,可考慮使用執行緒池技術
- 資料處理速度受限:資料接收和資料處理分離
[3] 解決步驟:
- recvfrom和資料處理函式不再順序執行一次只處理一個包。
- 邏輯上分離資料接收和處理:
- recvfrom負責接收資料並存儲,設定單獨計數1
- 資料處理函式負責從從儲存中拿資料並處理,設定單獨計數2
- 抓包執行緒一直處於執行狀態:while
- 資料處理可以等待喚醒
- 但資料接收需要一直進行,否則難免丟包
補充:TCP緩衝區檢視和修改命令
TCP接收緩衝區大小
- TCP接收緩衝區預設值:cat /proc/sys/net/ipv4/tcp_rmem
- 本系統:87380,約85K
- TCP接收緩衝區最大值:cat /proc/sys/net/core/rmem_max ,TCP最大可設定值的一半(與UDP同)
- 本系統:212992 = 208K,即最大值425984 = 416K。
- 已被我修改為:3145728 = 3M,即最大值6291456 = 6M
- TCP接收緩衝區最小值:sysctl -a | grep rmem
- 本系統:net.ipv4.udp_rmem_min = 4096 = 2K
- 通用命令:cat /proc/sys/net/ipv4/tcp_rmem
- 4096 87380 6291456,依次為最小值、預設值、最大值
- TCP接收緩衝區預設值:cat /proc/sys/net/ipv4/tcp_rmem
TCP接收緩衝區大小
- TCP傳送緩衝區預設值:cat /proc/sys/net/ipv4/tcp_wmem
- 本系統:16384 = 16K
- TCP傳送緩衝區最大值:cat /proc/sys/net/core/wmem_max,TCP最大可設定值的一半
- 本系統:212992 = 208K,即最大值425984 = 416K
- TCP傳送緩衝區最小值:sysctl -a | grep wmem
- 本系統:net.ipv4.udp_wmem_min = 4096 = 2K
- 通用命令:cat /proc/sys/net/ipv4/tcp_wmem
- 4096 16384 4194304,依次為最小值、預設值、最大值
注:通過cat /proc/sys/net/ipv4/tcp_wmem命令和cat /proc/sys/net/core/wmem_max命令得出的TCP傳送緩衝區最大值不一致,我覺得應該以cat /proc/sys/net/ipv4/tcp_wmem為準,即最大值為4M。
- TCP傳送緩衝區預設值:cat /proc/sys/net/ipv4/tcp_wmem
Acknowledgements:
http://blog.csdn.net/getnextwindow/article/details/24328117?utm_source=tuicool&utm_medium=referral
http://blog.csdn.net/ljh0302/article/details/49738191
http://blog.csdn.net/herecles/article/details/8146017
http://blog.sina.com.cn/s/blog_858820890100sfpx.html
http://bbs.csdn.net/topics/390958727
2017.07.29
文中錯誤和不足部分,歡迎交流指正