客戶端套接字的超時(timeout)就是指在客戶端通過Socket和服務器進行通訊的過程中,由於網絡延遲,網絡阻塞等原因,造成服務器並未及時響應客戶端的一種現象。在一段時間後,客戶端由於未收到服務端的響應而拋出一個超時錯誤; 其中客戶端所等待的時間就是超時時間。
由於生產超時錯誤的一端都是被動端;也就是說,這一端是在接收數據,而不是發送數據。對於客戶端Socket來說,只有兩個地方是在接收數據;一個是在連接服務器時;另一個是在連接服務器成功後,接收服務器發過來的數據時。因此,客戶端超時也分為兩種類型:連接超時和讀取數據超時。
一、連接超時
這種超時在前面的例子中已經使用過。在Socket類中只有通過connect方法的第二個參數才能指定連接超時的時間。由於使用connect方法連接服務器必須要指定IP和端口;因此,無效的IP或端口將會引發連接超時錯誤。
二、讀取數據超時
在連接服務器成功後,Socket所做的最重要的兩件事就是“接收數據”和“發送數據”;而在接收數據時可能因為網絡延遲、網絡阻塞等原因,客戶端一直處於等待狀態;而客戶端在等待一段時間後,如果服務器還沒有發送數據到客戶端,那麼客戶端Socket將會拋出一個超時錯誤。
我們可以通過Socket類的setSoTimeout方法來設置讀取數據超時的時間;時間的單位是毫秒。這個方法必須在讀取數據之前調用才會生效。如果將超時時間設為0,則不使用超時時間;也就是說,客戶端什麼時候和服務器斷開,將完全取決於服務端程序的超時設置。如下面的語句將讀取數據超時時間設為5秒。
Socket socket = new Socket();
socket.setSoTimeout(5000);
socket.connect(… …);
socket.getInputStream().read();
要注意的是不要將設置連接超時和讀取數據超時設置得太小,如果值太小,如100,可能會造成服務器的數據還沒來得及發過來,客戶端就拋出超時錯誤的現象。下面的代碼給出了一個用於測試連接超時和讀取數據超時的例子。
package mynet;
import java.net.*;
public class SocketTimeout
{
public static void main(String[] args)
{
long time1 = 0, time2 = 0;
Socket socket = new Socket();
try
{
if (args.length < 4)
{
System.out.println("參數錯誤!");
return;
}
time1 = System.currentTimeMillis();
socket.connect(new InetSocketAddress(args[0], Integer
.parseInt(args[1])), Integer.parseInt(args[2]));
socket.setSoTimeout(Integer.parseInt(args[3]));
time1 = System.currentTimeMillis();
socket.getInputStream().read();
}
catch (SocketTimeoutException e)
{
if (!socket.isClosed() && socket.isConnected())
System.out.println("讀取數據超時!");
else
System.out.println("連接超時");
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
finally
{
time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
}
}
}
SocketTimeout類的main方法需要4個參數:IP(域名)、端口、連接超時、讀取數據超時。下面讓我們來用一組數據來測試這個例子。
測試1 :無效IP引發的超時錯誤
執行如下命令:
java mynet.SocketTimeout 192.168.18.24 80 3000 5000
運行結果:
連接超時
3045
注意:192.168.18.24是一個無效的IP;如果這個IP在網絡環境中存在,請換其它的無效的IP.在這個測試用例中不能將無效的IP換成無效的域名;這是因為如果使用了域名來連接服務器,Java會先通過DNS將域名映射成相應的IP;如果這個域名是無效的,在映射的過程中就會出錯;因此,程序也就不會執行連接服務器操作,自然也就不會拋出“連接超時”錯誤了。
測試2 :無效端口引發的超時錯誤
執行如下命令:
java mynet.SocketTimeout www.ptpress.com.cn 8888 3000 5000
運行結果:
連接超時
3075
測試3 :讀取數據超時錯誤
執行如下命令:
java mynet.SocketTimeout www.ptpress.com.cn 80 3000 5000
運行結果:
讀取數據超時!
5008
測試4 :將讀取數據超時設為0的效果
執行如下命令:
java mynet.SocketTimeout www.ptpress.com.cn 80 3000 0
運行結果:
Connection reset
131519
從前3個測試的輸出結果不難看出,每個測試用例都將連接超時和讀取數據超時分別設為3000和5000毫秒;而它們的運行結果並不是3000和5000毫秒,而是在所設定的超時時間的左右搖擺;這主要是因為系統所輸出的時間並不都是超時時間;如有一些時間是Java處理錯誤、向控制台輸出信息的時間。另外,由於系統計時的誤差,也會影響到超時時間的准確性。但不管怎樣,超時時間總會在所設定的時間周圍搖擺。
對於測試4,將讀取數據超時設為0後,SocketTimeout類經過了2分多鐘(131519毫秒)才拋出Connection reset錯誤。這個拋出錯誤的時間和服務端程序的超時設置有關;也就是這個錯誤是由於服務端程序主動將客戶端網絡連接斷開而產生的。