HttpClient超時設置setConnectionTimeout和setSoTimeout


http是基於TCP/IP進行通信的,tcp通過3次握手建立連接,並最終以4次揮手終止通信。

知乎上對三次握手和四次揮手有如下解釋:

作者:知乎用戶
鏈接:https://www.zhihu.com/question/67772889/answer/256760079
來源:知乎

http是應用層協議,主要依賴於運輸層TCP協議(HTTP協議沒有規定具體使用哪個運輸層協議)。
tcp連接建立和斷開方式涉及到客戶端和服務器端的端口,緩存等資源的分配與釋放問題。

建立連接時,主動連接方(客戶端)向服務器請求建立連接(SYN),服務器端收到后會給客戶端響應(ack),表示它這邊准備好連接了,但是他要確保客戶端收到了它的響應,於是需要第三個數據包 客戶端向服務器端確認(ack),這樣相互確認之后服務器端就可以給這條連接分配必要的端口、緩存等資源。 假設只有兩次數據包交換就分配資源,若服務器端響應客戶端連接請求的包丟失了,客戶端會認為服務器未響應,放棄本次連接請求,而服務器端之前分配的資源就會被閑置浪費。 更多次的報文交換是可以的,但是完全沒有必要。 三次交換是建立連接所需最少的數量。

斷開連接時, 任務先完成的一方(這里假設是客戶端)會向另一方(即服務端)發送斷開請求(FIN),表示自己這一方傳輸任務已經完成,服務器端收到后會響應。 但是這里 服務器端傳送給客戶端的數據可能還沒有完,他需要維持當前連接來完成剩余的傳輸任務,即客戶端還不能釋放當前連接所需的資源。當服務端數據傳輸任務完成后,他會向客戶端發送斷開請求(FIN),客戶端響應。 這樣雙方相互確認數據傳輸完成,可以斷開連接,分別釋放當前連接占用的系統資源,而不是提前釋放導致傳輸的數據丟失。   

TCP三次握手

TCP三次握手

TCP四次揮手

TCP四次揮手

 

 


httpConnection有兩個重要的屬性:http.connection.timeout和http.socket.timeout。connection timeout是建立連接的超時時間,socket timeout表示的是等待服務端響應數據的超時時間。

SocketTimeoutException 和 ConnectTimeoutException 均派生自 InterruptedIOException(IO被中斷異常、IO被阻斷異常)

 

 

commons-httpclient 3.1里HttpConnectionParams.java里有如下2個方法:

    /**
     * Sets the timeout until a connection is etablished. A value of zero 
     * means the timeout is not used. The default value is zero.
     * 
     * @param timeout Timeout in milliseconds.
     */
    public void setConnectionTimeout(int timeout) {
        setIntParameter(CONNECTION_TIMEOUT, timeout);
    }
     
     
    /**
     * Sets the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the 
     * timeout for waiting for data. A timeout value of zero is interpreted as an infinite 
     * timeout. This value is used when no socket timeout is set in the 
     * {@link HttpMethodParams HTTP method parameters}. 
     *
     * @param timeout Timeout in milliseconds
     */
    public void setSoTimeout(int timeout) {
        setIntParameter(SO_TIMEOUT, timeout);
    }

 

 


如下示例代碼,可以模擬ConnectTimeoutException和SocketTimeoutException

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;

import java.io.IOException;

public class HttpclientTimeoutTest {

    public static void main(String[] args) {
        HttpClient client = new HttpClient();

        HttpMethod method = new GetMethod("https://linkedin.com/company/stack-overflow");
        client.getHttpConnectionManager().getParams().setConnectionTimeout(10);
        client.getHttpConnectionManager().getParams().setSoTimeout(1000);
        System.out.println("begin..");
        long start = System.currentTimeMillis();
        try {
            int statusCode = client.executeMethod(method);
            System.out.println(statusCode);

            byte[] responseBody = null;
            responseBody = method.getResponseBody();
            String result = new String(responseBody);

            System.out.println(result);
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            System.out.println("end..Duration MS:" + (System.currentTimeMillis() - start));
        }
    }
}

 

 

這里,將建立連接的超時時間設置為小到10ms。即可復現出來ConnectTimeoutException---connect timed out。

begin..
org.apache.commons.httpclient.ConnectTimeoutException: The host did not accept the connection within timeout of 10 ms
	at org.apache.commons.httpclient.protocol.ReflectionSocketFactory.createSocket(ReflectionSocketFactory.java:155)
	at org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory.createSocket(SSLProtocolSocketFactory.java:130)
	at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
	at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
	at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
	at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
	at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
	at com.emax.paycenter.HttpclientTimeoutTest.main(HttpclientTimeoutTest.java:24)
Caused by: java.net.SocketTimeoutException: connect timed out
	at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
	at java.net.Socket.connect(Socket.java:579)
	at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:618)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.apache.commons.httpclient.protocol.ReflectionSocketFactory.createSocket(ReflectionSocketFactory.java:140)
	... 7 more
end..Duration MS: 1064
View Code

 

將讀取數據的響應超時時間設置為小到10ms,即可復現出來SocketTimeoutException---Read timed out。

begin..
java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:152)
	at java.net.SocketInputStream.read(SocketInputStream.java:122)
	at sun.security.ssl.InputRecord.readFully(InputRecord.java:442)
	at sun.security.ssl.InputRecord.read(InputRecord.java:480)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:927)
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
	at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:702)
	at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:122)
	at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
	at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
	at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
	at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
	at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
	at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
	at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
	at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
	at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
	at com.emax.paycenter.HttpclientTimeoutTest.main(HttpclientTimeoutTest.java:25)
end..Duration MS: 1516
View Code

 

 

具體應用中,需要以實際需要來做超時設置。

 

references:

文中分析了httpclient3.*的一些漏洞。推薦使用httpclient4.2.3。1. 將connection請求頭設置為close,則會關閉連接socket。(如果是keep-alive ,不會關閉);2、在使用httpclient3.1時(4.2.3沒問題),盡量不要調用 byte[] getResponseBody() :因為如果Content-Length沒設置或者傳輸的數據大於1M,會有大量冗余日志。

分析了TCP為什么需要3次握手,和為什么需要4次揮手


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM