技術筆記(12)網路資料傳輸問題
-
希望實現的功能或目標:
- 解決伺服器與客戶端進行資料傳輸時可能會遇到的問題
-
學習筆記:
-
粘包半包問題:
-
是TCP/IP協議中常見的現象,因為TCP傳送的資料是連續的位元組流,沒有固定的邊界
-
粘包:發生在傳送方連續傳送多個小資料包時,接收方可能會將這些包合併成一個大的資料包來處理。因為TCP為了網路效率,會盡量減少傳送次數,所以可能多個小包合併傳送
-
半包:發生在傳送方傳送的資料包長度大於接收方的緩衝區長度時,接收方無法一次性完整接收整個資料包,導致資料包被分割成多個部分接收。
-
解決方法:
- 固定長度:規定好每個資料包統一長度,不足部分用特殊字元填充
- 分隔符:在相鄰資料包之間新增特定字元如&,來區分不同的資料包
- 長度欄位:在資料包的頭部新增一個欄位來表明資料包的長度,這樣接收方就可以根據長度欄位來正確的分割和重組資料流
-
-
大小端問題:
- 大端模式:高位位元組放在記憶體的低地址端,低位位元組放在記憶體的高地址端
- 小端模式:與大端相反,高放高,低放低
- 大小端問題通常在不同計算架構之間傳輸資料時需要考慮,例如:網路協議通常使用大端模式,而許多個人計算機則使用小端模式。
- 即大端模式的網路傳輸預設高位位元組位於低地址,會從低地址開始傳輸
-
執行緒衝突問題:
- 也稱竟態條件,只在多執行緒程式中,兩個以上執行緒同時訪問和修改同一個共享資源而沒有適當同步,導致程式執行結果出現不確定性和錯誤。
- 解決:通常需要使用互斥鎖、訊號量或其他同步機制來確保在任何時刻只有一個執行緒能夠訪問特定的資源。關鍵是確保對共享資源的訪問在任何時候都是互斥的。
- 針對於客戶端傳送資訊時,在BeginSend和SendCallback中去操控傳送緩衝區各相關變數,可能產生的執行緒衝突。我們可以透過實現一個訊息傳送寫入佇列。然後透過對這個佇列上鎖來避免,將Send和SendCallback中與訊息佇列相關的操作都寫入鎖內。
-
-
實現過程中產生的疑惑:
- 大小端命名的由來?
- 單個位元組和儲存單元的內部是否也會有大小端類似的順序問題?
- BitConverter. IsLittleEndian的作用?
- 鎖的具體原理?
-
對疑惑的解答:
-
命名由來:
- 源自小說《格列佛遊記》中兩個派別對於如何正確開啟雞蛋的爭論。一方認為應該從雞蛋的大端開始開啟,而另一方則堅持從小端開啟。這個比喻被用來描述位元組序的概念。
- 我個人的理解是,雞蛋往往是一頭重,一頭輕,如果我們把分量重的那一頭比作高位位元組,把分量輕的那一頭比作低位位元組,那麼,正立的雞蛋就是大端模式,因為我們把分量重(高位位元組)的部分放在底部(低位地址),而倒立的雞蛋就是小端模式,即分量輕(低位位元組)的部分放在底部(低位地址)。這樣一想,還挺生動形象,且也便於個人學習和記憶。
-
位元沒有大小端的問題:
- 原因:在硬體層面,位(bit)的物理儲存順序通常由CPU的設計決定,對於軟體開發者而言是透明不可見的。開發者主要關注的是位元組級別的資料表示。
-
BitConverter. IsLittleEndian:
- 表示當前計算機體系結構位元組順序的靜態屬性。它的值是隻讀的,且在執行時確定。
-
鎖:
-
定義:一種用於控制多執行緒程式對共享資源的訪問的機制。
-
作用:確保任意時刻,只有一個執行緒可以訪問特定的程式碼塊和資源。
-
原理:
- 基本原理是互斥。當一個執行緒想要訪問被鎖保護的資源時,它必須首先獲得鎖。如果鎖已經被另一個執行緒所持有,請求鎖的執行緒就會被阻塞,直到鎖被釋放。當持有鎖的執行緒完成了對資源的操作後,它會釋放鎖。
- 在硬體層面,鎖的實現通常依賴於原子操作,即那些在執行過程中不會被其他執行緒中斷的操作。這些原子操作由CPU直接支援,例如現代處理器提供
Compare-and-Swap
這樣的原子指令,為實現鎖的關鍵。 - (原子操作:指在多執行緒環境中,一個或一系列的操作在執行過程中不會被其他執行緒中斷,它們作為一個整體一次性完成,確保資料的一致性和系統的穩定性。這種操作一旦開始,就會執行到結束,中間不會發生任何的上下文切換。另外,能在一個CPU指令中完成而不被中斷的操作,也可以認為是原子的。)
-
型別:
- 互斥鎖:最基本鎖型別,保證同一時間只有一個執行緒可以訪問資源
- 讀寫鎖:允許多個讀操作,而寫操作會獨佔鎖
- 自旋鎖:讓執行緒在獲得鎖之前在一個迴圈中等待,直到鎖可用
-
注意:避免死鎖、活鎖和資源飢餓等問題
-
-