http://www.cnblogs.com/OpenCoder/p/5089258.html
IE中Keep-Alive機制引起的錯誤
我們知道Http協議是基於TCP/IP連接的,也就是說客戶端瀏覽器向服務器發出一個Http請求並得到響應是要建立一條TCP/IP連接的,但是如果每發出一個Http請求客戶端就要向服務器端建立一條TCP/IP連接的話,這樣效率會非常低,所以在Http協議中有一個叫做Keep-Alive的機制,如果在服務器端(IIS或Apache等Web服務器)啟動Keep-Alive后,客戶端瀏覽器向Web服務器發出的Http請求被響應后建立的TCP/IP連接並不會被立刻釋放掉,而是會存在一段時間,這個時間叫Keep-Alive Timeout,如果在Keep-Alive Timeout時間內沒有新的Http請求從客戶端發送到服務器端,那么建立的TCP/IP連接就會被服務器端釋放掉,如果在Keep-Alive Timeout時間內又有Http請求從客戶端瀏覽器發送到了服務器端,那么服務器端又會等待Keep-Alive Timeout時間后才會釋放TCP/IP連接,這樣大大提高了TCP/IP連接在Http協議中的復用率提高系統性能。這個過程可以用下面這張圖來解釋,左邊是沒有啟用Keep-Alive時Http請求的狀況,可以看到客戶端每發出一次Http請求,都會和服務器端建立(open)一個TCP/IP連接然后釋放(close)鏈接,右邊是啟用Keep-Alive后Http請求的狀況,客戶端發出的3個Http請求都復用同一個TCP/IP連接。
清楚了Keep-Alive是什么東西我們再來說說Keep-Alive中的另一個概念叫max,啟用了Keep-Alive的Http連接,在Http響應頭中一般會包含兩個參數timeout和max,timeout就是我們在上面提到的Keep-Alive Timeout時間,在這個時間內如果沒有新的Http請求發送到服務器端那么TCP/IP連接就會被服務器端釋放掉,而max是表示TCP/IP連接還可以復用的次數,每當TCP/IP連接被Keep-Alive機制復用一次max的值就會減1,如果max等於0了,表示該TCP/IP連接不允許再被復用了,服務器端就會釋放TCP/IP連接,如果客戶端后面又有Http請求發送到服務器端,客戶端和服務器端就必須重新建立一個TCP/IP連接。下圖就是服務器端啟用Keep-Alive機制后,客戶端瀏覽器收到的Http響應頭,里面包含timeout和max兩個字段值。
從上面我們知道了如果服務器端啟用了Keep-Alive機制,那么有兩種情況建立的TCP/IP連接會被正常釋放(非正常釋放就不提了,比如關機了,斷網了等。。。),一是在Keep-Alive Timeout時間內沒有新的Http請求復用TCP/IP連接,那么TCP/IP連接就會被服務器端釋放,二是當建立的TCP/IP連接被復用了max次后,max的值已經為0,服務器端也會釋放TCP/IP連接。
當服務器端啟用Keep-Alive機制后,會向客戶端瀏覽器發送兩個參數timeout和max,所以客戶端瀏覽器就會知道Keep-Alive機制下建立的TCP/IP連接多久后會被服務器端釋放掉,TCP/IP連接還能復用多少次,如果瀏覽器發現TCP/IP連接已經被服務器端釋放掉了,那么瀏覽器就會和服務器端建立一個新的TCP/IP連接來發送后續的Http請求。但是有一種情況會導致IE瀏覽器不知道服務器端釋放了TCP/IP連接,而去復用TCP/IP連接導致:“SCRIPT7002: XMLHttpRequest: 網絡錯誤 0x2ef3, 由於出現錯誤 00002ef3 而導致此項操作無法完成” 錯誤發生。
我們來舉個例子,還是下面這個截圖,當IE瀏覽器收到這個Http響應標頭之后就知道服務器端啟用了Keep-Alive機制,並且Keep-Alive機制的timeout時間是5秒,最大復用次數為100次。
現在假如我們有個頁面使用javascript的setInterval函數每過5秒鍾就向服務器端發送一個Ajax請求,那么IE瀏覽器在向服務器端發送Ajax請求的時候就有可能會去復用服務器端已經被釋放掉的TCP/IP連接。因為IE瀏覽器發送Ajax請求的時間間隔是5秒,而服務器端Keep-Alive機制timeout時間也是5秒,那么IE瀏覽器發出Ajax請求和服務器端釋放TCP/IP連接這兩個事情就很有可能在同一個時間點上發生。我們設想一下當IE瀏覽器發出第一次Ajax請求時和服務器端建立了一個TCP/IP連接,由於服務器端啟用了Keep-Alive機制,該TCP/IP連接在5秒后才會被服務器端釋放掉,而在5秒后IE瀏覽器開始發送第二次Ajax請求的時候認為5秒時間還沒到還可以復用第一次Ajax請求建立的TCP/IP連接,所以當IE瀏覽器發出第二次Ajax請求的時候就沿用了第一次Ajax請求建立的TCP/IP連接,但是當第二次發出的Ajax請求到達服務器端的時候已經是第一次Ajax請求到達服務器端的時間的5秒后了(我們假定Ajax請求會在網絡上傳輸1秒到達服務器端,那么當第二次Ajax請求到達服務器端的時候距離第一次Ajax請求已經有6秒的時間了),所以TCP/IP連接已經被服務器端釋放掉了,那么IE瀏覽器發出的第二次Ajax請求就會被服務器端拋棄,服務器端不會對IE瀏覽器發出的第二次Ajax請求作出任何響應,所以IE瀏覽器就會拋出:“SCRIPT7002: XMLHttpRequest: 網絡錯誤 0x2ef3, 由於出現錯誤 00002ef3 而導致此項操作無法完成” 錯誤。我們可以用下面一個時序圖來描述這個過程:
經測試這個問題只會在IE(測試環境為IE11)瀏覽器上發生,在Firefox和Chrome上並無此問題,說明Firefox和Chrome在發出Http請求之前會去檢測Keep-Alive機制建立的TCP/IP連接是否已經被釋放掉了,如果釋放掉了Firefox和Chrome不會報錯而是向服務器端開啟一個新的TCP/IP連接,避免復用已釋放的TCP/IP連接這種情況發生,而IE瀏覽器就比較笨了,一旦復用了已釋放的TCP/IP連接就報錯了。。。
說了這么多那么怎樣避免IE瀏覽器因為復用已釋放的TCP/IP連接而報錯呢?最保險的方法是關閉服務器端的Keep-Alive機制,盡管這樣系統效率會有所下降但是這樣IE瀏覽器肯定不會因為復用被釋放的連接而報錯了。。。另外也可以建議用戶不要使用IE瀏覽器,這肯定是不現實的。。。。最后如果把Keep-Alive機制的timeout時間設置長一點而不是像本例一樣只設置到5秒,也可以大大降低IE瀏覽器復用被釋放連接的風險,但是不能夠保證肯定是安全的。
下面介紹下怎樣在IIS7中關閉Keep-Alive機制,另外設置Keep-Alive機制的timeout時間
IIS7關閉Keep-Alive機制,進入站點設置,然后選擇HTTP Response Headers設置,點擊Set Common Headers,將Enable HTTP keep-alive復選框取消選中即可。
IIS7設置Keep-Alive機制的timeout時間,在站點設置中點擊高級設置(Advanced Settings),然后在彈出的窗口中設置Connection Time-out的值即可,默認是120秒即2分鍾。