今天在windows上除錯FTP下載檔案時,出險執行緒假死,程式碼如下:
if (inputStream != null) {
byte[] data = null;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
data = new byte[inputStream.available()];
int len = 0;
while ((len = inputStream.read(data)) != -1) {
outStream.write(data, 0, len);
}
data = outStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
re = encoder.encodeToString(data);
}
通過列印日誌排查到while死迴圈,檢視read方法原始碼註釋:
/* <p> If the length of <code>b</code> is zero, then no bytes are read and
* <code>0</code> is returned; otherwise, there is an attempt to read at
* least one byte. If no byte is available because the stream is at the
* end of the file, the value <code>-1</code> is returned; otherwise, at
* least one byte is read and stored into <code>b</code>.
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
在date位元組陣列長度為1時,read方法返回0,while出現死迴圈,原因是因為網路傳輸資料不及時,導致inputStream.available()方法未取到本地記憶體的位元組資料返回0。
由於下載的檔案大小不是很大,預設給了一個2048大小的位元組陣列,於是做了以下優化:
if (inputStream != null) {
byte[] data = null;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
if (inputStream.available() == 0) {
data = new byte[2048];
} else {
data = new byte[inputStream.available()];
} int len = 0;
while ((len = inputStream.read(data)) != -1) {
outStream.write(data, 0, len);
}
data = outStream.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
re = encoder.encodeToString(data);
}
再次除錯,問題解決。
但是,在將程式部署到Linux伺服器上依然假死,
問題分析:
FTPClient呼叫retrieveFileStream執行緒假死
FTPClient.listFiles()或者FTPClient.retrieveFile()方法時,就停止在那裡,什麼反應都沒有,出現假死狀 態。於是各種網上資料查詢
FTP的兩種傳輸模式:主動模式和被動模式
在呼叫這兩個方法之前,呼叫 FTPClient.enterLocalPassiveMode();這個方法的意思就是每次資料連線之前,ftp client告訴ftp server開通一個埠來傳輸資料。因為ftp server可能每次開啟不同的埠來傳輸資料,但是在linux上,由於防火牆安全限制,可能某些埠沒有開啟,所以就出現阻塞。
解決方法:ftpClient.enterLocalPassiveMode(); //開啟本地被動模式,此程式碼設定在登陸之後或者之前都可以。
同樣,如果上傳的話需要enterRemotePassiveMode()//開啟遠端被動傳輸模式
感謝網路上的大神們的分享,不然又得鑽牛角了。