http是基於TCP/IP進行通訊的,tcp經過3次握手創建鏈接,並最終以4次揮手終止通訊。html
知乎上對三次握手和四次揮手有以下解釋:java
做者:知乎用戶
連接:https://www.zhihu.com/question/67772889/answer/256760079
來源:知乎
http是應用層協議,主要依賴於運輸層TCP協議(HTTP協議沒有規定具體使用哪一個運輸層協議)。
tcp鏈接創建和斷開方式涉及到客戶端和服務器端的端口,緩存等資源的分配與釋放問題。apache創建鏈接時,主動鏈接方(客戶端)向服務器請求創建鏈接(SYN),服務器端收到後會給客戶端響應(ack),表示它這邊準備好鏈接了,可是他要確保客戶端收到了它的響應,因而須要第三個數據包 客戶端向服務器端確認(ack),這樣相互確認以後服務器端就能夠給這條鏈接分配必要的端口、緩存等資源。 假設只有兩次數據包交換就分配資源,若服務器端響應客戶端鏈接請求的包丟失了,客戶端會認爲服務器未響應,放棄本次鏈接請求,而服務器端以前分配的資源就會被閒置浪費。 更屢次的報文交換是能夠的,可是徹底沒有必要。 三次交換是創建鏈接所需最少的數量。緩存
斷開鏈接時, 任務先完成的一方(這裏假設是客戶端)會向另外一方(即服務端)發送斷開請求(FIN),表示本身這一方傳輸任務已經完成,服務器端收到後會響應。 可是這裏 服務器端傳送給客戶端的數據可能尚未完,他須要維持當前鏈接來完成剩餘的傳輸任務,即客戶端還不能釋放當前鏈接所需的資源。當服務端數據傳輸任務完成後,他會向客戶端發送斷開請求(FIN),客戶端響應。 這樣雙方相互確認數據傳輸完成,能夠斷開鏈接,分別釋放當前鏈接佔用的系統資源,而不是提早釋放致使傳輸的數據丟失。
httpConnection有兩個重要的屬性:http.connection.timeout和http.socket.timeout。connection timeout是創建鏈接的超時時間,socket timeout表示的是等待服務端響應數據的超時時間。服務器
SocketTimeoutException 和 ConnectTimeoutException 均派生自 InterruptedIOException(IO被中斷異常、IO被阻斷異常)socket
commons-httpclient 3.1裏HttpConnectionParams.java裏有以下2個方法:tcp
/** * 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和SocketTimeoutExceptionide
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。post
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
將讀取數據的響應超時時間設置爲小到10ms,便可復現出來SocketTimeoutException---Read timed out。url
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
具體應用中,須要以實際須要來作超時設置。
references:
文中分析了httpclient3.*的一些漏洞。推薦使用httpclient4.2.3。1. 將connection請求頭設置爲close,則會關閉鏈接socket。(若是是keep-alive ,不會關閉);二、在使用httpclient3.1時(4.2.3沒問題),儘可能不要調用 byte[] getResponseBody() :由於若是Content-Length沒設置或者傳輸的數據大於1M,會有大量冗餘日誌。
分析了TCP爲何須要3次握手,和爲何須要4次揮手