神奇好望角 The Magical Cape of Good Hope

          庸人不必自擾,智者何需千慮?
          posts - 26, comments - 50, trackbacks - 0, articles - 11
            BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

          前面已經看到,Socket 類的 getInputStream()getOutStream() 方法分別獲取套接字的輸入流和輸出流。輸入流用來讀取遠端發送過來的數據,輸出流則用來向遠端發送數據。

          輸入流

          使用套接字的輸入流讀取數據時,當前線程會進入阻塞狀態,直到套接字收到一些數據為止(亦即套接字的接收緩沖區有可用數據)。該輸入流的 available() 方法只是返回接收緩沖區的可用字節數量,不可能知道遠端還要發送多少字節。使用輸入流的時候,最好先將它包裝為一個 BufferedInputStream,因為讀取接收緩沖區將導致 JVM 和底層系統之間的切換,應當盡量減少切換次數以提高性能。BufferedInputStream 的緩沖區大小最好設為套接字接收緩沖區的大小。

          如果直接調用輸入流的 close() 方法來關閉它,則將導致套接字被關閉。對此,Socket 類提供了一個 shutdownInput() 方法來禁用輸入流。調用該方法后,每次讀操作都將返回 EOF,無法再讀取遠端發送的數據。對這個 EOF 的檢測,不同的輸入流包裝體現出不同的結果,可能讀到 -1 個字節,可能讀到的字符串為 null,還可能收到一個 EOFException 等等。禁用輸入流后,遠端輸出流的行為是平臺相關的:

          • 在 BSD 平臺上,遠端的發送的數據能正常接收,然后直接丟棄。遠端無法知道本端的輸入流已禁用。這和 JDK 文檔描述的行為一致。
          • 在 WINSOCK 平臺上,遠端發送數據將會導致“連接被重置”的錯誤。
          • 在 Linux 平臺上,遠端發送的數據能繼續接收,直到套接字輸入緩沖區填滿,之后遠端再也無法發送數據(若使用阻塞模式則進入死鎖)。

          禁用輸入流這種技術并不常用。

          輸出流

          套接字的輸出操作實際上僅僅將數據寫到發送緩沖區內,當發送緩沖區填滿且上次的發送成功后,由底層系統負責發送。如果發送緩沖區的剩余空間不夠,當前線程就會阻塞。和輸入流類似,最好將輸出流包裝為 BufferedOutputStream。

          如果套接字的雙發都使用 ObjectInputStreamObjectOutputStream 來讀寫 Java 對象,則必須先創建 ObjectOutputStream,因為 ObjectInputStream 在構造的時候會試圖讀取對象頭部,如果雙發都先創建 ObjectInputStream,則會互相等待對方的輸出,造成死鎖:

          // 創建的順序不能顛倒!
          ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
          ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
              

          類似于輸入流,關閉輸出流也導致關閉套接字,所以 Socket 類同樣提供了一個 shutdownOutput() 來禁用輸出流。禁用輸出流后,已寫入發送緩沖區的數據會正常發送,之后的任何寫操作都會導致 IOException,且遠端的輸入流始終會讀到 EOF。禁用輸出流非常有用,例如套接字的雙發都在發送完畢數據后禁用輸入流,然后雙方都會收到 EOF,從而知道數據已經全部交換完畢,可以安全關閉套接字。直接關閉套接字會同時關閉輸入流和輸出流,且斷開連接,達不到這種效果。

          使用流的阻塞套接字的優缺點

          如果要使用流進行輸入和輸出,就只能用阻塞模式的套接字。這里總結一下阻塞套接字的優缺點。先看看優點:

          1. 編程模型簡單,非常適合初學者上手。
          2. 以裝飾器模式設計的 Java I/O 使得開發人員可以輕松地從 I/O 流讀寫任何類型的數據。

          但在性能方面有致命的缺點:

          1. 由于服務器套接字接受連接,以及套接字的讀寫都會阻塞,性能低下。
          2. 如果不對 I/O 流手動進行緩沖,則可能造成一次只處理一個字節,性能低下。
          3. 服務器套接字每次只能接受一個連接,導致 JVM 和底層系統之間頻繁的調用切換,性能低下。

          下一篇文章開始探討使用基于 NIO 的套接字通道和緩沖區實現伸縮性更強的 TCP 套接字。


          評論

          # 為什么用socket的getinputStream之后用bufferedreader包裝向遠程發送數據,只能發送一次,用flush()不行了,只能關閉輸出流才能接收到呢?[未登錄]  回復  更多評論   

          2013-06-18 23:22 by 123
          socket的流問題!
          主站蜘蛛池模板: 陇南市| 许昌县| 西乡县| 南昌市| 滦平县| 大连市| 鄂托克旗| 根河市| 台安县| 乡宁县| 天祝| 望谟县| 永春县| 根河市| 香格里拉县| 呼图壁县| 乐东| 邹城市| 丰宁| 资溪县| 佛坪县| 时尚| 五指山市| 长子县| 永春县| 余干县| 新建县| 湛江市| 西昌市| 南康市| 岳池县| 广安市| 咸宁市| 黄平县| 梓潼县| 辉县市| 盐城市| 河间市| 宜君县| 汝城县| 长宁区|