Socket 編程中,TCP 流的結束標志與粘包問題


因為 TCP 本身是無邊界的協議,因此它並沒有結束標志,也無法分包。「包」的界定,是更上層的協議的事了(比如 HTTP)。

socket和文件不一樣,從文件中讀,讀到末尾就到達流的結尾了,所以會返回-1或null,循環結束,但是socket是連接兩個主機的橋梁,一端無法知道另一端到底還有沒有數據要傳輸。
socket如果不關閉的話,read之類的阻塞函數會一直等待它發送數據,就是所謂的阻塞。

如果發送的東西非常多必須要用循環讀,可以有以下解決方案:

  • 調用socket的 shutdownOutput 方法(Java)關閉輸出流,該方法的文檔說明為,將此套接字的輸出流置於“流的末尾”,這樣另一端的輸入流上的read操作就會返回-1。
  • 約定結束標志,當讀到該結束標志時退出不再read。 (Http 的 Transfer-Encoding: Chunked 首部,表示將以一個 length 為 0 的 chunk 做結束標志)
  • 設置超時(timeout),會在設置的超時時間到達后拋出SocketTimeoutException異常而不再阻塞。
  • 雙方定義好通信協議,在協議頭部約定好數據的長度。當讀取到的長度等於這個長度時就不再繼續調用read方法。(Http 的 content-length 首部,會給出主體的長度)

而如果需要發送多個相互獨立的內容,內容之間就需要有明確的分界,方法有:

  1. 像 multipart-form 一樣,使用 boundary 字串做分割,使用 Content-Type 等首部做內容標識。
  2. 用二進制幀做分割,在幀首部定義好幀的長度和其他信息。

參考

關於java網絡編程中獲取輸入流中數據的問題
Linux編程之socket:tcp流協議產生的粘包問題及解決方法
終端如何與服務器通信——玩轉通信協議(源碼下載)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM