记录一次Socket的异常:InputStream.read()阻塞问题
起先是在Socket編程時(shí),服務(wù)端取得客戶(hù)端發(fā)送的數(shù)據(jù),但是在InputStream.read()的時(shí)候,一直停在那,然后取了解了read方法才知道阻塞問(wèn)題
代碼示例:
//端口數(shù)據(jù)取得 byte[] b = new byte[1024]; in.read(b);//阻塞地方 String contents = new String(b).trim();//trim去除多余空格,否則,讀進(jìn)來(lái)的是byte[1024]個(gè)占位字節(jié)Logger.getLogger(Constant.TASK).info("端口數(shù)據(jù)取得:" + contents);JAVA里的IO 目前有兩種,一種是早期發(fā)布的I/O模型,也就是所謂的BIO(Blocking I/O);另一種是JDK1.4里發(fā)布的基于 多路復(fù)用實(shí)現(xiàn)的NIO。
?
阻塞型 I/O,主要阻塞在兩個(gè)地方:
第一:在調(diào)用InutStream.read 方法是阻塞的,它會(huì)一直等到數(shù)據(jù)到來(lái)時(shí)(或超時(shí))才會(huì)返回;第二:在調(diào)用ServerSocket.accept()方法時(shí),也會(huì)一直阻塞到有客戶(hù)端連接才會(huì)返回;
?
目前大部分的客戶(hù)端服務(wù)端的網(wǎng)絡(luò)應(yīng)用軟件的早期版本的I/O都是使用阻塞型的I/O實(shí)現(xiàn)。
阻塞型的I/O 存在以下幾點(diǎn)問(wèn)題:
首先,InputStream.read()方法在其緩存區(qū)未滿(mǎn)時(shí),會(huì)造成阻塞,只有一定的數(shù)據(jù)填滿(mǎn)了緩存區(qū)或者客戶(hù)端關(guān)閉了套接字,方法才會(huì)返回。
其次,會(huì)產(chǎn)生大量的垃圾,BufferedReader創(chuàng)建了緩存區(qū)來(lái)從套接字中讀入數(shù)據(jù),但是同樣創(chuàng)建了一些字符串存儲(chǔ)這些數(shù)據(jù)。這些String很快變成垃圾需要回收。
類(lèi)似的,讀寫(xiě)操作被阻塞而且向流中一次寫(xiě)入一個(gè)字符會(huì)造成效率低下,所以應(yīng)該使用緩存區(qū),但一旦使用緩存,流又會(huì)產(chǎn)生更多是垃圾。
另外,通常在JAVA中處理阻塞I/O要用到線(xiàn)程(大量的線(xiàn)程),一般是實(shí)現(xiàn)一個(gè)線(xiàn)程池來(lái)處理請(qǐng)求。線(xiàn)程使得服務(wù)器可以處理多個(gè)連接,但是他們同樣也引發(fā)了許多問(wèn)題。每個(gè)線(xiàn)程擁有
自己的棧空間并且占用一些CPU時(shí)間,耗費(fèi)很大,而且很多時(shí)間是浪費(fèi)了阻塞I/O操作上,沒(méi)有有效利用CPU.次接收的操作是否結(jié)束了.???
可以看一下源碼
public abstract class InputStream extends Object implements Closeable此抽象類(lèi)是表示字節(jié)輸入流的所有類(lèi)的超類(lèi)。
需要定義?InputStream?的子類(lèi)的應(yīng)用程序必須始終提供返回下一個(gè)輸入字節(jié)的方法。
個(gè)人理解,這種對(duì)象的概念有點(diǎn)像需要數(shù)據(jù)傳輸雙方之間的一個(gè)通道,這個(gè)通道負(fù)責(zé)接收數(shù)據(jù)(與之對(duì)應(yīng)還有OutPutStream 負(fù)責(zé)發(fā)送數(shù)據(jù))。
InputStream 中的read方法用于讀取數(shù)據(jù),方法有3個(gè)重載。
read()從輸入流讀取下一個(gè)數(shù)據(jù)字節(jié)。 read(byte[] b)從輸入流中讀取一定數(shù)量的字節(jié)并將其存儲(chǔ)在緩沖區(qū)數(shù)組 b 中。 read(byte[] b, int off, int len)將輸入流中最多 len 個(gè)數(shù)據(jù)字節(jié)讀入字節(jié)數(shù)組。其中InputStream.read()方法,這個(gè)方法是從流里每次只讀取讀取一個(gè)字節(jié),效率會(huì)非常低。????
更好的方法是用InputStream.read(byte[] b)或者InputStream.read(byte[] b,int off,int len)方法,一次讀取多個(gè)字節(jié)。
這里有一點(diǎn)需要特別注意:read?方法在輸入數(shù)據(jù)可用、檢測(cè)到文件末尾或者拋出異常前,此方法一直阻塞。
Socket流這里還存在另外一個(gè)問(wèn)題,socket流和文件流不太一樣,文件流很容易知道文件末尾,到了文件末尾,直接就把流close掉就OK了。但是socket 流不一樣,你無(wú)法知道它什么時(shí)候到末尾,所以連接一直保持著,流也一直保持阻塞狀態(tài)。即使用了帶參數(shù)的read方法,返回了有效數(shù)據(jù),但其實(shí)流仍然沒(méi)有關(guān)閉,處于阻塞狀態(tài)。
針對(duì)這種請(qǐng)情況,一般就需要通信的雙方約定數(shù)據(jù)傳輸?shù)膮f(xié)議了。
①比如,約定消息的頭部首先明確此次傳輸數(shù)據(jù)的大小。這樣服務(wù)端就可以有目的性的讀取數(shù)據(jù)
如果采用一個(gè)一個(gè)字節(jié)讀取的話(huà),就需要先兩方約定好。不然就會(huì)造成read一直在阻塞狀態(tài)。
②采用InputStrea.shutdownOutput(),在客戶(hù)端的關(guān)閉Stream輸出,這樣服務(wù)端就不會(huì)一直在等待輸入流的輸入
這時(shí)候可以采用一次性讀取的read(b[])方法,注意的是b[]要不小于傳輸過(guò)來(lái)的數(shù)據(jù)大小,不然只會(huì)讀取其中一部分。
總結(jié)
以上是生活随笔為你收集整理的记录一次Socket的异常:InputStream.read()阻塞问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2022年中国青少年STEAM教育研究报
- 下一篇: 面试中,答不出产品方法论?4个方法教给你