搞懂Socket通信(二)

Android唐浮 2021-08-15 18:16:43 阅读数:20

本文一共[544]字,预计阅读时长:1分钟~
搞懂 socket 通信

這是我參與8月更文挑戰的第15天,活動詳情查看:8月更文挑戰

搞懂Socket通信(二)

上篇文章講到,在長連接通信中可以使用多種三方框架,當第三方框架滿足不了設計場景的時候,我們又該怎麼辦,該篇文章講解Socket TCP的特性和原理。

一、應用場景

1.1 場景一

大家都知道,在瀏覽器輸入一個網址,在有網絡的情况下,網頁上就能渲染出來頁面。

這種模式是大概是這樣,這段連接在請求完畢之後結束了。

輸入網址
請求服務器
服務器返回數據
頁面渲染
結束

為了滿足該場景,有了Http協議,請求時開啟TCP連接,請求完畢時關閉TCP連接。

1.2 場景二

A同學 發送消息 給B同學,B同學收到消息。

A同學發送
服務器接收到
服務器把消息轉給B同學
B同學收到消息
結束

為了滿足該場景,就要有雙向連接,客戶端可以和服務端發送消息,服務端也可以向客戶端發送連接;為了能即時接到消息,就必須要實時性,就用到了長連接。

所以確立這種場景的通信是雙向長連接

二、TCP特性

TCP比特於傳輸層,相應的還有UDP傳輸協議,TCP為什麼可靠,因為它有連接時的三次握手和斷開時的四次揮手,保障發送數據的可靠性,相應的它會增加一點時間,犧牲一點效率。

為了保證數據的可靠性,我們都是用TCP傳輸,當我們使用Socket操作TCP時,也無需關心TCP複雜的底層邏輯。

那麼TCP有什麼特性

  1. TCP是全雙工通信,將占用兩個計算機之間的通信線路,直到它被一方或雙方關閉為止

  2. TCP是可靠的,三次握手和四次揮手 + 包序號可以證明

  3. TCP是流,不是包,像水流一樣,需要分割才能取出正確數據

三、使用Socket TCP該注意什麼

3.1 連接狀態

也可以把這個章節的標題改為其它標題,比如

為什麼需要心跳?

為什麼服務端斷網時,客戶端依舊顯示連接狀態?

正常情况

在正常情况下,無論是客戶端還是服務端,在正常執行 socket.close()的情况下,對方是可以收到回調、可以監聽到對方離線的狀態。

强制斷網情况

在强制斷網的情况下,對方是無法監聽到斷開狀態的

這也就是“Socket TCP 服務端斷網時,客戶端依舊顯示連接狀態”的現象。

使用socket進行測試:

  1. 客戶端斷網時,客戶端接到斷開回調

  2. 服務端手動關閉時,客戶端接到斷網回調

  3. 服務端運行突然斷網時,客戶端接收不到斷網的回調(分很多場景,參考:

www.cnblogs.com/549294286/p…

為什麼在服務端斷網時接不到回調呢?

這是因為TCP建立的通道被破壞,無法進行斷開的四次揮手(因為網絡斷了),造成了所謂的死鏈接。

這個時候是就需要心跳機制,來互相確認是否在線。

3.2 發送狀態

同樣,標題也可以改為

為什麼我發送了消息,Socket tcp回調成功了,對方卻沒有收到?

長連接時靠什麼保障消息的可靠性?

為什麼有些Socket框架提供了發送結果的回調卻並不可靠?

由於在斷網情况下,TCP已經斷開了,但是另一方沒有及時進行斷開。

但這個時候,如果發送消息給對象,得到的發送回調卻是發送成功。

這是因為,當通道破壞時,另一方沒有及時收到,而發送成功的連接僅僅代錶數據被放進了緩沖區,並不代錶數據已經發送給了對方

所以這個回調結果只是代錶 我發出去了,至於我發出去之後的路上遇到了什麼,就不得而知了,所以只能有參考作用。

那麼如果需要把消息做的可靠,還是應該,在接收到消息時,告訴對方這條消息接收成功。

3.3 粘包分包

概念

粘包:

當發送的字節數據包比較小且頻繁發送時,Socket內部會將字節數據進行粘包處理,既將頻繁發送的小字節數據打包成 一個整包進行發送,降低內存的消耗。

分包:

當發送的字節數據包比較大時,Socket內部會將發送的字節數據進行分包處理,降低內存和性能的消耗。

為什麼會出現粘包

發生粘包分包實際上是在TCP傳輸協議對數據的優化,TCP是“流”協議,傳輸的過程就像流水一樣沒有邊界,沒有界限,而實際上我們只需要取出我們發送的部分即可;這裏說明下UDP是"數據包"協議,所以在UDP中不存在粘包分包的情况。

為什麼會出現分包

在傳輸過程中,TCP為了保護網絡(也稱為流量控制),並不是接收到什麼就傳遞什麼,而是根據一系列的限制决定發送多少數據出去,這些限制有:用戶緩沖區(接收發送緩沖區)、TCP底層緩沖區(各系統下錶現會不同)、MTU(最大傳輸單元,屬於數據鏈路層)等等

例子解釋


當前發送方發送了兩個包,兩個包的內容如下:
123456789
ABCDEFGH
複制代碼

我們希望接收方的情况是:收到兩個包,第一個包為:123456789,第二個包為:ABCDEFGH。但是在粘包和分包出現的情况就達不到預期情况。

粘包情况

兩個包在很短的時間間隔內發送,比如在0.1秒內發送了這兩個包,如果包長度足够的話,那麼接收方只會接收到一個包,如下:


123456789ABCDEFGH
複制代碼

分包情况

假設包的長度最長設置為5字節(較極端的假設,一般長度設置為1000到1500之間),那麼在沒有粘包的情况下,接收方就會收到4個包,如下:


12345
6789
ABCDE
FGH
複制代碼

如何處理粘包分包?

在數據層面處理:

  1. 約定結束符,遇到結束符時分割數據。

  2. 發送數據時約定數據長度,獲取數據時先接到長度,然後根據長度分割數據。

  3. 發送數據末尾拼接換行符,使用BufferedReaderrd.readLine(),原理同上。

技術選型:

  1. 使用成熟的socket框架,很多框架會幫忙處理合包的操作。

  2. 使用WebSocket,WebSocket是一個message-based的協議,它可以自動將數據分片,並且自動將分片的數據組裝。

版权声明:本文为[Android唐浮]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815181419012X.html