當(dāng)柳上原的風(fēng)吹向天際的時(shí)候...

          真正的快樂(lè)來(lái)源于創(chuàng)造

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks
          轉(zhuǎn)載自:
          http://www.cnblogs.com/Mygirl/archive/2012/03/27/2419971.html


               什么是HTTP長(zhǎng)連接? HTTP長(zhǎng)連接,與一般每次發(fā)起http請(qǐng)求或響應(yīng)都要建立一個(gè)tcp連接不同,http長(zhǎng)連接利用同一個(gè)tcp連接處理多個(gè)http請(qǐng)求和響應(yīng),也叫 HTTP keep-alive,或者h(yuǎn)ttp連接重用。使用http長(zhǎng)連接可以提高h(yuǎn)ttp請(qǐng)求/響應(yīng)的性能。

               使用http長(zhǎng)連接有很多好處,包括:
          更少的建立和關(guān)閉tcp連接,可以減少網(wǎng)絡(luò)流量。 因?yàn)橐呀⒌膖cp握手,減少后續(xù)請(qǐng)求的延時(shí)。 長(zhǎng)時(shí)間的連接讓tcp有充足的時(shí)間判斷網(wǎng)絡(luò)的擁塞情況,方便做出下步操作。

              這些優(yōu)點(diǎn)在使用https連接時(shí)更顯著。可以減少多次建立高消耗的SSL/TLS握手。 在HTTP/1.1中,默認(rèn)使用的是長(zhǎng)連接方式。客戶端默認(rèn)服務(wù)端會(huì)保持長(zhǎng)連接,即便返回錯(cuò)誤響應(yīng);除非明確指示不使用長(zhǎng)連接。同時(shí),協(xié)議中也指定了客戶 端可以發(fā)送關(guān)閉信號(hào)到服務(wù)端來(lái)關(guān)閉TCP連接。

              怎樣是連接可以重用? 因?yàn)門CP是基于流的協(xié)議,所以HTTP協(xié)議需要有一種方式來(lái)指示前一個(gè)響應(yīng)的結(jié)束和后一個(gè)響應(yīng)的開(kāi)始來(lái)重用已建立的連接。所以,它要求連接中傳輸?shù)男畔?必須有自定義的消息長(zhǎng)度。自定義消息長(zhǎng)度可以通過(guò)設(shè)置 Content-Length 消息頭,若傳輸編碼的實(shí)體內(nèi)容塊,則每個(gè)數(shù)據(jù)塊的標(biāo)明數(shù)據(jù)塊的大小,而且響應(yīng)體也是以一個(gè)特殊的數(shù)據(jù)塊結(jié)束。

              若中間存在代理服務(wù)器將會(huì)如何? 因?yàn)殚L(zhǎng)連接僅占用一條傳輸鏈路,所以代理服務(wù)器能否正確得與客戶端和服務(wù)器端(或者其他代理服務(wù)器)發(fā)送長(zhǎng)連接或非長(zhǎng)連接的信號(hào)尤為重要。但是HTTP的 客戶端或服務(wù)器端來(lái)看,代理服務(wù)器對(duì)他們來(lái)說(shuō)是透明的,即便長(zhǎng)連接是需要關(guān)注的。

               當(dāng)前的JDK如何處理Keep-Alive? JDK同時(shí)支持HTTP/1.1 和 HTTP/1.0。 當(dāng)應(yīng)用程序讀取完響應(yīng)體內(nèi)容后或者調(diào)用 close() 關(guān)閉了URLConnection.getInputStream()返回的流,JDK中的HTTP協(xié)議句柄將關(guān)閉連接,并將連接放到連接緩存中,以便后 面的HTTP請(qǐng)求使用。 對(duì)HTTP keep-Alive 的支持是透明的。但是,你也可以通過(guò)系統(tǒng)屬性http.keepAlive和http.maxConnections以及HTTP/1.1協(xié)議中的特定的 請(qǐng)求響應(yīng)頭來(lái)控制。控制Keep-Alive表現(xiàn)的系統(tǒng)屬性有:
              http.keepAlive=<布爾值> 默認(rèn): true 指定長(zhǎng)連接是否支持
              http.maxConnections=<整數(shù)> 默認(rèn): 5 指定對(duì)同一個(gè)服務(wù)器保持的長(zhǎng)連接的最大個(gè)數(shù)。
              影響長(zhǎng)連接的HTTP header是: Connection: close 如果請(qǐng)求或響應(yīng)中的Connection header被指定為close,表示在當(dāng)前請(qǐng)求或響應(yīng)完成后將關(guān)閉TCP連接。

               JDK中的當(dāng)前實(shí)現(xiàn)不支持緩存響應(yīng)體,所以應(yīng)用程序必須讀取完響應(yīng)體內(nèi)容或者調(diào)用close()關(guān)閉流并丟棄未讀內(nèi)容來(lái)重用連接。此外,當(dāng)前實(shí)現(xiàn)在清理連接時(shí)并未使用阻塞讀,這就意味這如果響應(yīng)體不可用,連接將不能被重用。

              JDK1.5中的新特性 當(dāng)應(yīng)用接收到400或500的HTTP響應(yīng)時(shí),它將忽略IOException 而另發(fā)一個(gè)HTTP 請(qǐng)求。這種情況下,底層的TCP連接將不會(huì)再保持,因?yàn)轫憫?yīng)內(nèi)容還在等待被讀取,socket 連接未清理,不能被重用。應(yīng)用可以在捕獲IOException 以后調(diào)用HttpURLConnection.getErrorStream() ,讀取響應(yīng)內(nèi)容然后關(guān)閉流。但是現(xiàn)存的應(yīng)用沒(méi)有這么做,不能體現(xiàn)出長(zhǎng)連接的優(yōu)勢(shì)。為了解決這個(gè)問(wèn)題,介紹下workaround。
          當(dāng)響應(yīng)體的狀態(tài)碼大于或等于400的時(shí)候,workaround 將在一定時(shí)間內(nèi)緩存一定數(shù)量的響應(yīng)內(nèi)容,釋放底層的socket連接來(lái)重用。基本原理是當(dāng)響應(yīng)狀態(tài)碼大于或等于400時(shí),服務(wù)器端會(huì)發(fā)送一個(gè)簡(jiǎn)短的響應(yīng)體來(lái)指明連接誰(shuí)以及如何恢復(fù)連接。

             

          下面介紹一些SUN實(shí)現(xiàn)中的特定屬性來(lái)幫助接收到錯(cuò)誤響應(yīng)體后清理連接: 主要的一個(gè)是: sun.net.http.errorstream.enableBuffering=<布爾值> 默認(rèn): false

               當(dāng)上面屬性設(shè)置為true后,在接收到響應(yīng)碼大于或等于400是,HTTP 句柄將嘗試緩存響應(yīng)內(nèi)容。釋放底層的socket連接來(lái)重用。所以,即便應(yīng)用不調(diào)用getErrorStream()來(lái)讀取響應(yīng)內(nèi)容,或者調(diào)用 close()關(guān)閉流,底層的socket連接也將保持連接狀態(tài)。
          下面的兩個(gè)系統(tǒng)屬性是為了更進(jìn)一步控制錯(cuò)誤流的緩存行為: sun.net.http.errorstream.timeout=<int> in 毫秒 默認(rèn): 300 毫秒

               你如何做可以保持連接為連接狀態(tài)呢? 不要忽略響應(yīng)體而丟棄連接。這樣會(huì)是TCP連接閑置,當(dāng)不再被引用后將會(huì)被垃圾回收器回收。 如果getInputStream()返回成功,讀取全部響應(yīng)內(nèi)容。如果拋出IOException ,捕獲異常并調(diào)用getErrorStream() 讀取響應(yīng)內(nèi)容(如果存在響應(yīng)內(nèi)容)。
          即便你對(duì)響應(yīng)內(nèi)容不感興趣,也要讀取它,以便清理連接。但是,如果響應(yīng)內(nèi)容很長(zhǎng),你讀取到開(kāi)始部分后就不感興趣了,可以調(diào)用close()來(lái)關(guān)閉流。值得注意的是,其他部分的數(shù)據(jù)已在讀取中,所以連接將不能被清理進(jìn)而被重用。
          下面是一個(gè)基于上面建議的代碼樣例:

          try
              URL a = new URL(args[0]); 
              URLConnection urlc = a.openConnection(); 
              is = conn.getInputStream(); 
              int ret = 0; 
              while ((ret = is.read(buf)) > 0) { 
                processBuf(buf); 
              }
              // close the inputstream
              is.close();
          } catch (IOException e) {
              try {
                  respCode = ((HttpURLConnection)conn).getResponseCode();
                  es = ((HttpURLConnection)conn).getErrorStream();
                  int ret = 0;
                  // read the response body
                  while ((ret = es.read(buf)) > 0) {
                      processBuf(buf);
                  }
                  // close the errorstream
                  es.close();
              } catch(IOException ex) {
                  // deal with the exception
              }
          }

            如果你預(yù)先就對(duì)響應(yīng)內(nèi)容不感興趣,你可以使用HEAD 請(qǐng)求來(lái)代替GET 請(qǐng)求。例如,獲取web資源的meta信息或者測(cè)試它的有效性,可訪問(wèn)性以及最近的修改。下面是代碼片段:

          URL a = new URL(args[0]);
          URLConnection urlc = a.openConnection();
          HttpURLConnection httpc = (HttpURLConnection)urlc;
          // only interested in the length of the resource
          httpc.setRequestMethod("HEAD");
          int len = httpc.getContentLength();
          posted on 2013-04-10 21:16 何楊 閱讀(320) 評(píng)論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 法库县| 贵德县| 四平市| 东明县| 恭城| 宜州市| 象山县| 广西| 辰溪县| 峨边| 德庆县| 鸡西市| 忻城县| 柏乡县| 邹平县| 富川| 阜新| 德州市| 射阳县| 双辽市| 上蔡县| 本溪| 麻城市| 扬州市| 库伦旗| 卓资县| 浦县| 清涧县| 武冈市| 大方县| 红河县| 乌拉特前旗| 青海省| 蛟河市| 清徐县| 青河县| 灵山县| 夏邑县| 稷山县| 油尖旺区| 普兰店市|