由於 TCP 自己是無邊界的協議,所以它並無結束標誌,也沒法分包。html
socket和文件不同,從文件中讀,讀到末尾就到達流的結尾了,因此會返回-1或null,循環結束,可是socket是鏈接兩個主機的橋樑,一端沒法知道另外一端到底還有沒有數據要傳輸。
socket若是不關閉的話,read之類的阻塞函數會一直等待它發送數據,就是所謂的阻塞。java
若是發送的東西很是多必需要用循環讀,能夠有如下解決方案:編程
- 調用socket的 shutdownOutput 方法(Java)關閉輸出流,該方法的文檔說明爲,將此套接字的輸出流置於「流的末尾」,這樣另外一端的輸入流上的read操做就會返回-1。
- 約定結束標誌,當讀到該結束標誌時退出再也不read。 (Http 的 Transfer-Encoding: Chunked 首部,表示將以一個 length 爲 0 的 chunk 作結束標誌)
- 設置超時(timeout),會在設置的超時時間到達後拋出SocketTimeoutException異常而再也不阻塞。
- 雙方定義好通訊協議,在協議頭部約定好數據的長度。當讀取到的長度等於這個長度時就再也不繼續調用read方法。(Http 的 content-length 首部,會給出主體的長度)
而若是須要發送多個相互獨立的內容,內容之間就須要有明確的分界,方法有:服務器
- 像 multipart-form 同樣,使用 boundary 字串作分割,使用 Content-Type 等首部作內容標識。
- 用二進制幀作分割,在幀首部定義好幀的長度和其餘信息。
參考
關於java網絡編程中獲取輸入流中數據的問題
Linux編程之socket:tcp流協議產生的粘包問題及解決方法
終端如何與服務器通訊——玩轉通訊協議(源碼下載)網絡