1.getsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv_out, sizeof(struct timeval));
問題:Bad address,報錯,errno =14 ,EFAULT:optval指向的內存並非有效的進程空間
解決辦法:getsockopt的第四個參數需要通過指針傳遞。
int sendbuf = 0; socklen_t opt_len = sizeof(sendbuf); set_ret = getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (int *)&sendbuf, &opt_len); if(set_ret != 0) { printf("%d,%s",errno,strerror(errno)); } else { printf("[wzh socket sendbuf]=>%p,sendbuf:%d,ret:%d\n",&sendbuf,sendbuf,set_ret); } set_ret = getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (int *)&sendbuf, &opt_len); if(set_ret != 0) { printf("%d,%s",errno,strerror(errno)); } else { printf("[wzh socket rcvbuf]=>%p,rcvbuf:%d,ret:%d\n",&sendbuf,sendbuf,set_ret); }
打印結果如下:
在connect()之前調用
[wzh socket sendbuf]=>0x7fc2af70,sendbuf:16384,ret:0 //16KB
[wzh socket rcvbuf]=>0x7fc2af70,rcvbuf:87380,ret:0 //85KB
在conect()之后調用
[wzh socket sendbuf]=>0x7fa4d770,sendbuf:20680 //20680是一個奇怪的數字,20k應該等於 20480
[wzh socket rcvbuf]=>0x7fa4d770,rcvbuf:87380
2.使用系統命令獲取TCP的發送和接收緩沖區大小:
TCP發送緩沖區查看
[root@mvt:~]# cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 274624
最小值 默認 最大值
4KB 16KB 268KB
TCP接收緩存區查看
[root@mvt:~]# cat /proc/sys/net/ipv4/tcp_rmem
4096 87380 274624
最小值 默認 最大值
4KB 85KB 268KB
3.setsockopt設置套接字發送緩沖區的大小
int nSendBuf=4*1024; setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
打印信息:
[before connect socket sendbuf]=>0x7f89c234,sendbuf:16384,ret:0
[before connect socket sendbuf]=>0x7f89c234,rcvbuf:87380,ret:0
[after connect socket sendbuf]=>0x7f89c234,sendbuf:8192,ret:0 //發現設置的大小是設置的2倍
[after connect socket sendbuf]=>0x7f89c234,rcvbuf:87380,ret:0
查詢接收緩沖區和發送緩沖區最大值的位置:
[root@mvt:~]# cat /proc/sys/net/core/rmem_max
163840
[root@mvt:~]# cat /proc/sys/net/core/wmem_max
163840
1.當設置的值nSendBuf > 最大值2*sysctl_wmem_max,則設置為最大值的2倍:2*sysctl_wmem_max;
int nSendBuf=163840*3; setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
[before connect socket sendbuf]=>0x7fd9de14,sendbuf:16384,ret:0
[before connect socket sendbuf]=>0x7fd9de14,rcvbuf:87380,ret:0
connect success 31
[after connect socket sendbuf]=>0x7fd9de14,sendbuf:327680,ret:0
[after connect socket sendbuf]=>0x7fd9de14,rcvbuf:87380,ret:0
2.當設置的值的nSendBuf*2 <最小值,則設置成最小值:SOCK_MIN_SNDBUF;//SOCK_MIN_SNDBUF為2048,2KB
int nSendBuf=16; setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
[before connect socket sendbuf]=>0x7fc95db4,sendbuf:16384,ret:0
[before connect socket sendbuf]=>0x7fc95db4,rcvbuf:87380,ret:0
connect success 31
[after connect socket sendbuf]=>0x7fc95db4,sendbuf:2048,ret:0
[after connect socket sendbuf]=>0x7fc95db4,rcvbuf:87380,ret:0
3.當設置的值nSendBuf< 最大值2*sysctl_wmem_max,且 val*2> SOCK_MIN_SNDBUF, 則設置成2*nSendBuf
int nSendBuf=100*1024; setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
[before connect socket sendbuf]=>0x7f94a7f4,sendbuf:16384,ret:0
[before connect socket sendbuf]=>0x7f94a7f4,rcvbuf:87380,ret:0
connect success 31
[after connect socket sendbuf]=>0x7f94a7f4,sendbuf:204800,ret:0
[after connect socket sendbuf]=>0x7f94a7f4,rcvbuf:87380,ret:0
4.通過設置sendbuf的大小來測試網線插拔數據接收的影響
0.發送數據速度較快,發送端在發送較多的的數據之后,會暫停一會兒時間的發送。
1.設置sendbuf為4KB(這是當前系統中的兩個相機之間傳輸圖像的buf大小)
int nSendBuf=2*1024; setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
每拔掉一次網線,接收數據就會有影響,數據產生變化。
2.設置sendbuf為16KB(這是當前系統中的兩個相機之間傳輸圖像的buf大小)
int nSendBuf = 16384/2;
setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
插拔網線5次,對比數據,有一次收到的數據不准確
3.設置sendbuf為320KB(這是當前系統中的兩個相機之間傳輸圖像的buf大小)
int nSendBuf=163840*3;
setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
第一次:插拔5次:(其中3次跳出[ 219.485132] libphy: 0:00 - Link is Down)數據正常
第二次:插拔10次:(其中5次跳出[ 219.485132] libphy: 0:00 - Link is Down)出現一次數據接收不正常
第三次:插拔10次正常速度:數據正常
第三次:插拔10次正常速度:有一次數據接收不正常
4.更換內核版本(對網線斷開的感知大於5S)
a.更換內核周需要修改網卡啟動相關的指令(/system/init/init.sh)
#active mvt0 ... brctl addbr mvt0;brctl addif mvt0 eth1; brctl addif mvt0 eth0; ifconfig eth0 down; ifconfig eth1 down; ifconfig eth0 0.0.0.0 promisc; ifconfig eth1 0.0.0.0 promisc; ifconfig mvt0 192.168.1.10 promisc ifconfig eth0 down;ifconfig eth1 down chmod +x /system/init/set_mac /system/init/set_mac ifconfig eth0 up;ifconfig eth1 up
b.需要修改內存相關的參數(/system/init/init.sh)
echo "4096 327680 599872" > /proc/sys/net/ipv4/tcp_wmem echo "327680" >/proc/sys/net/core/wmem_max
c.內核版本
Linux version 3.10.14__isvp_swan_1.0__+ (yejilong@MVT-CPL) (gcc version 4.7.2 (Ingenic r2.3.3 2016.12) ) #4 PREEMPT Tue Mar 29 14:26:55 CST 2022
1.設置sendbuf為320KB
[after connect socket sendbuf]=>0x7fd5cc34,sendbuf:327680,ret:0//320KB
插拔5次正常速度:數據有一次不正常
2.設置sendbuf為640KB
[after connect socket sendbuf]=>0x7f958654,sendbuf:655360,ret:0//640KB
第一次:插拔5次,每次2秒內:數據正常
第二次插拔5次,每次2-3秒:數據正常
第三次插拔10次,每次2-4秒: 數據正常
第三次插拔10次,每次2-4秒: 數據正常
數據正常:指的是沒有觸發重傳
數據異常:指的是出發了TCP重傳
數據正常和數據異常:收到的數據均完整,TCP重傳機制當前超時時間為9分鍾。
TCP緩沖區
每一個TCP套接字都有一個發送緩沖區,這個緩沖區是在內核中的.當我們調用write將數據寫入套接字的時候,數據被傳入
內核,放入套接字發送緩沖區(大小可以由SO_SNDBUF來設定).如果緩沖區已滿,那么write函數將被阻塞,進程被投入睡眠狀態.
直到數據全部被傳入內核的套接字緩沖區中.需要注意的是,當write返回的時候並不代表對端已經收到了這些數據,只是說明這些
數據被寫到了內核中的緩沖區里面.
當數據到達內核中以后,內核中的TCP程序會對其進行一次加工,將數據以MSS(maximum segment size)大小分塊,並給每個數據塊
安上一個TCP首部,隨后發給IP程序.
IP程序可能會對數據進行進一步的分片,取決於MSS的大小.有的系統在實現中使用了路徑MTU發現機制,會盡量減少分片的情況.通用的
做法就是給數據包加上IP頭,按照其目的IP地址查找路由表確定網絡接口,然后傳遞給數據鏈路層.
數據鏈路層中會維護一個隊列,如果隊列已滿,則會沿協議棧返回一個錯誤,最終由TCP捕獲這個錯誤,並重傳該數據.
TCP緩沖區一個很重要的問題就是何時從套接字緩沖區中將已發送的數據丟棄.我們知道TCP是一個可靠的傳輸協議,所以TCP實現了關於確認數據
到達的相關機制,即當對端確認數據到達的ack返回時,則認定我們發送的數據已經被對方接受.此時內核中的套接字緩沖區才會將數據刪除.在確認的
ack到達之前,內核的套接字緩沖區中會為數據維護一個副本,防止數據由於損壞或者丟失造成重傳的時候內核已經將其刪除了.
UDP緩沖區
UDP套接字的緩沖機制就不在詳述,因為UDP是一個不可靠協議,所以它並不會維護一個真正的套接字緩沖區,而是直接沿協議棧向下傳遞,被復制到某種
格式的內核緩沖區當中.但是如果UDP打算傳送一個大於"緩沖區大小"(即SO_SNDBUF的大小)的數據報時,則會返回一個EMSGSIZE的錯誤.它經過內核中
的UDP和IP程序,並被發送到數據鏈路層的緩沖隊列當中,如果隊列已滿則返回一個ENOBUFS的錯誤(有的系統並不會返回錯誤).可以看到,UDP並不會為
用戶程序維護一個狀態.
5.監控sendbuf的容量變化
沒找到監控方式,
阻塞方式的send時,緩沖區滿時,阻塞;
非阻塞方式的send時,緩沖區滿時,返回錯誤碼;
(更新了內核后)手動插拔網線:
出現link is down: 文件可能不完整。
沒出現link is down: 讀寫均成功,測試3次(5-10次正常插拔) 尚未發現文件不完整。
tip:手動插拔存在時間的不確定性,需要根據寫程序來模擬網絡短時間(指定時間)斷開的情況
6.結論:
TCP數據傳輸並不會受網線插拔的影響。除非網線拔掉的時間已經超過了TCP的超時時間,而且不會去嘗試重傳了。