文章目錄
- 1、TCP沒考慮粘包分包
- 2、UDP沒考慮丟包
- 3、長連接沒考慮應用層心跳
- 4、大資料沒考慮分片和流量控制
- 5、客戶端沒考慮斷線重連
- 6、外網沒考慮加密通信
- 7、沒有處理SIGPIPE
- 8、大小端位元組序問題
- 9、多執行緒發送亂序問題
- 10、串包問題
1、TCP沒考慮粘包分包
TCP是面向連接的可靠協議,TCP是流式協議,創建TCP套接字的型別為SOCK_STREAM
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
很多同學面試時對書上的話背誦如流,在實際TCP編程中卻沒有處理粘包和分包的代碼,以為TCP也和UDP一樣,客戶端每send一次,服務端就會recv一次,在本機上測驗可能也沒有出現問題,一旦到了線上發生粘包和分包的情況就會導致邏輯出錯甚至程式崩潰,
解決方案:
1、使用流式決議器保存當前狀態,如http-parser就使用了流式決議;
2、使用快取push接收到的資料,判斷接收到完整的一幀資料再pop取出進行處理;
Tips:
在libhv中可通過hio_set_unpack設定拆包規則,支持固定包長、分隔符、頭部長度欄位三種常見的拆包方式,呼叫該介面設定拆包規則后,內部會根據拆包規則處理粘包與分包,保證回呼上來的是完整的一包資料,大大節省了上層處理粘包與分包的成本,
2、UDP沒考慮丟包
在一些追求低延時的場景,為了避免TCP三次握手,我們會考慮使用UDP協議,但是卻忽略了系統對丟包的容忍度,沒考慮到某個關鍵包丟失帶來的影響,沒有重傳重組機制,
解決方案:
結合FEC、KCP、UDT、QUIC等手段增強可靠性;
Tips:
libhv計劃陸續集成FEC、KCP、UDT、QUIC等開源實作,歡迎有志之士加入;
3、長連接沒考慮應用層心跳
TCP連接不是指真的有一條物理的連接,而是通信雙方靠狀態來記錄維持的,從客戶端發起SYN請求開始,狀態就開始有序轉換了,如果不發包,我們也就無法感知對方是否掉線,雖然TCP協議本身有keepalive機制,但是默認的間隔時間特別久,也無法攜帶其它資訊,所以發送應用層心跳是非常有必要的,能快速感知掉線以便做出通知和處理,也能及時關閉fd,釋放相關資源,以節省開銷,
解決方案:
使用定時器發送心跳包,多長時間或者多少次沒有收到回應便斷開連接;
Tips:
在libhv中可通過hio_set_heartbeat設定心跳;
4、大資料沒考慮分片和流量控制
見過有人直接將幾十M、上百M甚至幾G的檔案直接讀到記憶體進行發送,試問你家記憶體TB級別的嗎,經的起這么消耗,另外不做發送速率控制和流量控制,可能會導致網路擁塞,
解決方案:
回圈從磁盤讀取少量資料到記憶體再發送,并做好流量控制;
Tips:
libhv中大檔案的發送示例可參考examples/httpd里的largeFileHandler;
5、客戶端沒考慮斷線重連
網路哪沒有個掉線的時候,如果沒有斷線重連機制,將會嚴重影響用戶體驗,試想你正在打游戲,突然掉線了,不給你自動重連,必須重新啟動應用程式,是不是很影響心情,
Tips:
在libhv中可通過TcpClient::setReconnect設定重連延時策略;
6、外網沒考慮加密通信
在外網環境不使用SSL/TLS加密通信,就猶如一個人在大街上裸奔,沒有絲毫隱私可言,安全系數為0,
解決方案:
1、集成openssl、gnutls、mbedtls等SSL/TLS加密通信庫;
2、在網關處使用SSL代理,如使用nginx做反向代理服務;
Tips:
在libhv中集成了openssl、gnutls、mbedtls等SSL/TLS加密通信庫,打開WITH_OPENSSL、WITH_GNUTLS、WITH_MBEDTLS選項編譯,通過hio_enable_ssl即可開啟SSL/TLS加密通信,
7、沒有處理SIGPIPE
當向已經收到RST的socket執行寫操作時,內核就會向行程發送一個SIGPIPE信號,該信號的默認行為是終止行程,通常的做法是忽略該信號,
signal(SIGPIPE, SIG_IGN);
8、大小端位元組序問題
計算機硬體有兩種存盤資料的方式:大端位元組序和小端位元組序,網路通信中我們一般使用大端位元組序,如果我們不按照對應的位元組序來編碼解碼,就會得到錯誤的值,
9、多執行緒發送亂序問題
TCP雖然保證重傳重組,但是我們自己要保證發送資料的有序性,特別是多執行緒發送時,即使加鎖我們也無法保證哪個執行緒先發送,除非每個發送的包都是獨立完整的一包,不分先后順序,否則就可能引發亂序問題,
解決方案:
通常不建議多執行緒發送,而是由一個執行緒來負責發送,
Tips:
libhv中的hio_write、hio_close是多執行緒安全的,這可以讓網路IO事件回圈執行緒里接收資料、拆包組包、反序列化后放入佇列,消費者執行緒從佇列里取出資料、處理后發送回應和關閉連接,變得更加簡單,
10、串包問題
串包即將本將發送給A的資料發送給了B,通常發生在服務器用fd1接受A的請求,A掉線后,B再上線了,POSIX標準要求每次打開檔案的時候必須是要當前最小可以的檔案描述符,于是又將fd1分配給了B,如果你繼續使用fd1給A發送資料就會發送到了B,
解決方案:
該問題發生的根本原因是使用fd作為了設備的標示,應該建立某種機制來確認socket句柄是否是你想發送的那一個,例如設備連接后通過登錄驗證攜帶uuid來唯一標示該設備,
Tips:
以上問題皆是我在撰寫libhv網路庫,以及在libhvQQ技術交流群(739352073)里給大家解答時,總結出的網路編程程序中十條最容易踩的坑,我稱之為網路編程十宗罪,名字取的比較駭人,也是為了警醒大家在網路編程程序中切勿觸犯以上條例,做到按例排查,寫出更為健壯的網路程式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/357073.html
標籤:其他
