http協議中headers,cookies、session、緩存等相關知識,發現些新知識點。 php
這篇文章注重結合PHP去理解這些內容,也就是比較注重實踐部分。 css
1、http headers html
NO1:對於web應用,用戶羣在客戶端 (各類瀏覽器)點擊任何一個鏈接向服務器發送http請求,這過程確定須要3次握手,創建鏈接,服務器響應返回數據。 nginx
每次請求都有頭部和實體部分,先看下面筆者監聽QQ空間的headers,QQ空間的緣由是它頭部內容比較全
程序員
?web
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
Request Headers:
GET http://user.qzone.qq.com/445235728 HTTP/1.1
Host: user.qzone.qq.com
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://qzone.qq.com/
Accept-Encoding:gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Cookie:o_cookie=445235728;(省略不少……)
If-Modified-Since: Wed, 13 Jun 2012 01:32:19 GMT
-----------------
Response Headers:
HTTP/1.1 200 OK
Connection:close
Server: QZHTTP-2.34.0
Date: Wed, 13 Jun 2012 02:59:31 GMT
Content-Encoding: gzip
Set-Cookie:login_time=61F0EEA02D704B1DBCF25166A74941B24F4BE24B205C466F;PATH=/; DOMAIN=qzone.qq.com
Set-Cookie:Loading=Yes;expires=Wed,13-Jun-201216:00:00GMT;PATH=/;DOMAIN=qzone.qq.com X-UA-Compatible: IE=Edge Last-Modified: Wed, 13 Jun 2012 02:59:31 GMT
Cache-Control: max-age=0, no-transform
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
|
客戶端向服務端發請求 headers 和服務端響應客戶端 headers 圖: 數據庫
經過圖片能夠看出: apache
一、客戶端請求headers包含了請求行和一些頭域。 瀏覽器
請求行:請求的方法 統一資源標識器(URL)協議版本 ------這三者用空格分開,最後換行回車(\r\n) 例如:GET http://user.qzone.qq.com/445235728 HTTP/1.1 緩存
各類頭域:這些頭域都是有關鍵字和鍵值成對組合,最後換行回車(\r\n)結束,這些頭域告訴服務器應該怎麼去響應以及自己一些信息。
二、服務器響應
狀態行:協議版本 響應狀態 狀態描述 ------這三者用空格分開,最後換行回車(\r\n) 例如:HTTP/1.1 200 OK
各類頭域:這些頭域也是有關鍵字和鍵值成對組合,最後換行回車(\r\n)結束,這些頭域告訴客戶端應該怎麼去響應以及自己一些信息。
NO2
這裏就不一一說每一個頭域的概念和做用,想了解的請看:http://www.phpben.com/?post=34 如今介紹幾個認爲重要、在一些網站上的測試數據、以及請求返回各頭域php代碼實現
測試時間:2012.6.14前
測試對象:csdn 、cnbeta 、cnblos、騰訊(QQ空間、朋友網、新聞網)、新浪(微博、主頁)、人人網、百度、淘寶、優酷、土豆這些網站
(1) Connection頭域:這個頭域只有http/1.1纔有,默認是keep-alive值表示長鏈接,這樣的話就不用每請求一個資源好比圖片,css文件,js文件都要和服務器進行3此握手鍊接,這個在必定程度上彌補了http沒狀態的一個缺陷,減小鏈接服務器的時間。
查看測試網站Connection頭域發現騰訊QQ空間、騰訊新聞網、新浪主頁和微博,優酷和土豆Connection:close;除了這些其餘的都是Connection:keep-alive
爲何?
1、connection: keep-alive 能正常使用的一個前提條件是還要提供content-length的頭域告訴客戶端正文的長度。那計算正文長度是個問題,對於多內容,集羣服務器來講不是件易事。騰訊和新浪,優酷的這些都很難計算,對與工程師來講之間關閉了(默認是打開的)。
2、老服務器端不支持,對於騰訊,新浪這些老油條,服務器集羣很龐大,不免有些老舊的不支持長鏈接的,爲了一些兼容性問題,直接關閉了
Ps:這兩點緣由未求證過!^-^
用php headers(「Connection:keep-alive」);
(2) Content-Encoding頭域
Content-Encoding文檔的編碼(Encode)方法.
上述網站出了cnbeta不用gzip壓縮,優酷用deflate,其他都是。這也透漏一個重要信息,那就phper要掌握壓縮gzip傳輸技術。
Php能夠經過mod_gzip模塊來實現。代碼:ob_start("ob_gzhandler");
(3) Server頭域暴漏服務器重要的安全信息。
Csdn:Server:nginx/0.7.68 ------------版本都暴露
騰訊QQ空間:Server:QZHTTP-2.34.0--------某位tx朋友透漏這是內部本身開發的服務器,這個可夠安全
新浪微博:Server:apache -------------這個沒暴漏版本
鳳凰網:Server: nginx/0.8.53
人人網:Server:nginx/1.2.0
淘寶網:Tengine ---------這是淘寶內部技術團隊開發的web服務器,基於Nginx
cnblogs博客園:Server:Microsoft-IIS/7.5
騰訊朋友網:Tencent/PWS ---------騰訊內部開發
騰訊新聞網:Server:squid/3.1.18
優酷網:Server: fswww1-----------是否是內部就不清楚,至少筆者不知道什麼來的^_^
土豆網:Tengine/1.2.3
百度:server: BWS/1.0 ---------應該也是百度內部本身開發的服務器
很明顯Server頭域是返回服務器的信息,但也能夠說暴漏信息,面對這個問題,大公司就本身開發基於本身功能的內部服務器。
(4) X-Powered-By頭域可供修改,基於安全則能夠修改
X-Powered-By頭域反應什麼語言什麼版本執行後臺程序。這個能夠同個header函數修改
header("X-Powered-By:acb");
(5) Cache-control、expires、last-modified等重要頭域
Cache-control:指定請求和響應遵循的緩存機制。在請求消息或響應消息中設置Cache-Control並不會修改另外一個消息處理過程當中的緩存處理過程。請求時的緩存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,響應消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
Php代碼實現:header("cache-control: abc");abc是上述指令值一個或多個,多個用’,’分開
Expires:告訴瀏覽器指明應該在何時認爲文檔已通過期,從而再也不緩存它。代碼實現:header("Expires:". date('D, d M Y H:i:s \G\M\T', time()+10));--------這個是把時間截轉化成格林時區字符串給expires頭域,這個顯示時間會比中國北京時間少8個小時,東8區的實現:header("Expires:". date('r', time()+10))
last-modified:這個是服務器返回給瀏覽器,瀏覽器下次請求則把該值賦給if-modified-since頭域傳給服務器,服務器就能夠根據此值判斷是否有改變,有則繼續運行下去,否者返回304 not modified。Php設置expires頭域同樣。
代碼:
1
2
3
4
5
6
7
8
9
10
11
|
<?php
if
(isset(
$_SERVER
[
'HTTP_IF_MODIFIED_SINCE'
]) && (time()-
strtotime
(
$_SERVER
[
'HTTP_IF_MODIFIED_SINCE'
]) < 10)) {
header(
"HTTP/1.1 304 Not Modified"
);
exit
;
}
header(
"Last-Modified: "
.
date
(
'D, d M Y H:i:s \G\M\T'
, time()) );或者header(
"Last-Modified: "
.
date
(
'r'
, time()) );
?>
|
前者是格林時間格式,後者是中國時間。須要注意的就是 php.ini 的時區 prc 則用後則,否者前者。筆者曾經試過在時區是 prc 的狀況下用了前者,致使 time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) <0 永遠成立,由於是負值。
注意:當請求頁面有session_start()的時候,則不論是否有expires、cache-control、last-modified設置,則返回給客戶端Cache-Control頭域爲Cache-Control:no-store, no-cache, must-revalidate Expires頭域 Expires:Thu, 19 Nov 1981 08:52:00 GMT。這個問題煩了筆者2天,都覺得php.ini 或是apache的問題。最後居然是session_start()的問題。
2、 瀏覽器緩存動態
前面介紹了http headers幾個告訴瀏覽器如何處理緩存。但不一樣瀏覽器處理各類頭域的方式不一樣,如下就是筆者。
Ps:各個瀏覽器監聽http headers的方法能夠查看:http://www.phpben.com/?post=76
(1) header(「cache-control: no-store」)
IE9 |
Google17.0 |
Firefox11 |
Maxthon3 |
|
點擊刷新鍵 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
地址欄回車 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
點擊後退鍵 |
同上 |
同上 |
同上 |
同上 |
(2) header(「cache-control: no-cache」)
IE9 |
Google17.0 |
Firefox11 |
Maxthon3 |
|
點擊刷新鍵 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
地址欄回車 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
點擊後退鍵 |
同上 |
From cache |
From cache |
同上 |
(3) header(「cache-control:bublic」)
IE9 |
Google17.0 |
Firefox11 |
Maxthon3 |
|
點擊刷新鍵 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
地址欄回車 |
from cache |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
點擊後退鍵 |
From cache |
From cache |
From cache |
同上 |
(4) header("cache-control:private"); header("cache-control: must-revalidate ")
IE9 |
Google17.0 |
Firefox11 |
Maxthon3 |
|
點擊刷新鍵 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
地址欄回車 |
除第一次外都是from cache |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
點擊後退鍵 |
From cache |
From cache |
From cache |
同上 |
(5) header("cache-control:max-age=num");num是秒數
IE9 |
Google17.0 |
Firefox11 |
Maxthon3 |
|
點擊刷新鍵 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
地址欄回車 |
秒數<num from cache |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
點擊後退鍵 |
From cache |
From cache |
From cache |
同上 |
(6) header("Expires:". date('D, d M Y H:i:s \G\M\T', time()+num)); num是秒數
IE9 |
Google17.0 |
Firefox11 |
Maxthon3 |
|
點擊刷新鍵 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
地址欄回車 |
秒數<num from cache |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
重發請求,返回200狀態 |
點擊後退鍵 |
From cache |
From cache |
From cache |
同上 |
(7) if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < num)) {
header("HTTP/1.1 304 Not Modified");
exit;
} header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );
IE9 |
Google17.0 |
Firefox11 |
Maxthon3 |
|
點擊刷新鍵 |
秒數<num 304 not modified |
秒數<num 304 not modified |
秒數<num 304 not modified |
重發請求,返回200狀態 |
地址欄回車 |
from cache |
秒數<num 304 not modified |
秒數<num 304 not modified |
重發請求,返回200狀態 |
點擊後退鍵 |
From cache |
From cache |
From cache |
同上 |
結論:
一、 刷新對於任何瀏覽器且不論是什麼cache-control,都會從新請求,通常返回是200,除非Last-Modified設置
二、 後退鍵除非no-cache; no-store外都是使用緩存
三、 Cache-control:no-store 在瀏覽器中任何操做都從新提交請求,包括後退
四、 遨遊3的緩存不好
五、 IE9 的緩存很強,因此用ie9調試的時候儘量點刷新而不是在地址欄回車
鑑於這種狀況,對於不一樣的應用(有些要緩存,有些常常更新)對於不一樣的國家各類瀏覽器份額,而哪一種緩存方式。中國IE比較多,加上360瀏覽器的加入(用IE內核),那就要主要參照IE瀏覽器。
但筆者仍是比較喜歡header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );這種方式。結合起來connection:keep-alive能讓緩存技術更成熟。
注意:
一、 也許你會問,用Cache-control:no-store或Cache-control:no-store,但調試頁面仍是沒原來的緩存。而後清除瀏覽器緩存關掉重啓瀏覽器, 緩存還在。這是由於你的web應用用了文件緩存如ecshop常出現這種狀況,這種狀況就要進web應用後臺刪除文件緩存。
二、 調試的時候儘量不要在地址欄回車,特別是IE,google還好一點,可是要知道此次的測試只是各個瀏覽器中的一個版本,因此調試的時候儘量點刷新按鈕。
三、 但在cache-control:max-age=num 和expires 一塊兒使用的時候,前者級別比較高,瀏覽器會忽略expires的設置。(上面沒給出測試內容)
3、 Session和cookies
Session 、cookies是程序員永遠討論的話題之一。
一、 簡單說一下cookies、session
(1) Cookies是保存在客戶端的小段文本,隨客戶端點每個請求發送該url下的全部cookies到服務器端。好比在谷歌瀏覽器下,打開ww.abc.com下的兩個文件,a.php包含cookies1和cookies2,b.php包含了cookies3和cookies4,那麼在a.php或b.php 點任意一個鏈接(固然是ww.abc.com服務器上的),瀏覽器就會把cookies1~4這4個cookies發送給服務器。可是若是在IE9有打開一個包含cookies5的c.php,哪門在google瀏覽器點擊鏈接是不會發送cookies5的。
(2) Session則保存服務器段,經過惟一的值sessionID來區別每個用戶。SessionID隨每一個鏈接請求發送到服務器,服務器根據sessionID來識別客戶端,再經過session 的key獲取session值。SessionID傳回服務器的實現方式能夠經過cookies和url回寫來實現。
注意:
一、 同一個瀏覽器打開同一個文件,如a.php ,或同時有設置session的兩個文件a.php、b.php sessionID則只有一個。(時間上不能是打開a.php 關閉瀏覽器再打開b.php)
二、 不一樣瀏覽器在同一時間打開贊成文件的sessionID也不同
三、 sessionID是服務器生成的不規則惟一字符串,如:
PHPSESSID=05dbfffd3453b7be02898fdca4fcd82b;------ PHPSESSID能夠經過php.ini中session.name來改變,因此筆者在監聽一些大型網站的時候查不出PHPSESSID,這是一個安全因素。
(3) cookies、session在php中的主要相關參數
(1) session.save_handler = 」files」
默認以文件方式存取session數據,若是想要使用自定義的處理器來存取session數據,好比數據庫,用」user」。
(2) session.use_cookies = 1 前面說到sessionID用cookies來實現,這裏就是,1表示用cookies
(3) session.use_trans_sid = 0 上面是用cookies來實現sessionID,這裏值如果1則使用url回寫方式,級別比session.use_cookies高
(4) session.use_only_cookies = 0 值爲1則sessionID只能夠用cookies實現,級別比前兩個高
(5) session.cache_expire =180 session 緩存過時的秒數
(6) session.gc_maxlifetime = 1440
設定保存的session文件生存期,超過此參數設定秒數後,保存的數據將被視爲’垃圾’並由垃圾回收程序清理。判斷標準是最後訪問數據的時間(對於FAT文件系統是最後刷新數據的時間)。若是多個腳本共享同一個session.save_path目錄但session.gc_maxlifetime不一樣,將以全部session.gc_maxlifetime指令中的最小值爲準。
(4) 圖說cookie 、ssession
php代碼以下
1
2
3
4
5
6
7
8
9
10
11
|
session_start();
$_SESSION
[
'favcolor'
] =
'green'
;
$_SESSION
[
'animal'
] =
'cat'
;
$_SESSION
[
'time'
] = time();
setcookie(
"cookie1"
,
"www.phpben.com"
,time()+3600*10);
setcookie(
"cookie2"
,
"www.phpben.com"
,time()+3600*10);
|
圖片:
結論:
1、 第一次請求是沒用cookies的,而第二次有PHPSESSID和兩個cookies是由於服務器第一請求返回這個三個cookies。
2、第二次請求比第一次多返回PHPSESSID這個cookies,在第二次則沒有了,直到session過時後從新設置。
二、 ;瀏覽器關掉cookies,session是否能夠正常運行?
前面說起sessionID的時候有兩種方式。
(1) cookies 方式,在session.use_trans_sid=0 and session.use_cookies = 1的狀況下使用。這種方法是每次瀏覽器端點每一個請求,都把sessionID發送到服務器。
(2) url回寫,session.use_only_cookies = 0 and session.use_trans_sid=1的狀況下,服務器會忽略session.use_trans_sid,在瀏覽器發hhtp請求後,服務器會在返回頁面內容中每一個鏈接後面加上PHPSESSID=05dbfffd3453b7be02898fdca4fcd82b (在php.ini沒改session.name,默認是PHPSESSID),這樣就算客戶端的瀏覽器禁止了cookies,同樣能實現session功能。
這裏來個測試:
在1.php文件代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?php
echo
'Welcome to page #1<br/>'
;
session_start();
$_SESSION
[
'favcolor'
] =
'green'
;
$_SESSION
[
'animal'
] =
'cat'
;
$_SESSION
[
'time'
] = time();
// Works if session cookie was accepted
echo
'<br /><a href="2.php">page 1 (這個href中沒SID參數)</a><br/>'
;
// Or maybe pass along the session id, if needed
echo
'<br /><a href="2.php?'
. SID .
'">page 2 (這個href中有SID參數)</a><br/>'
;
?>
|
在 2.php 文件代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?php
session_start();
echo
'Welcome to page #2<br />'
;
echo
$_SESSION
[
'favcolor'
],
'<br/>'
;
// green
echo
$_SESSION
[
'animal'
],
'<br/>'
;
// cat
echo
date
(
'Y m d H:i:s'
,
$_SESSION
[
'time'
]),
'<br/>'
;
// You may want to use SID here, like we did in page1.php
echo
'<br /><a href="1.php">return page 1</a>'
;
?>
|
情景1:沒禁用瀏覽器的cookies(用cookies實現session),則在2.php能正常輸出
情景2:禁用用瀏覽器的cookies且在php.ini開啓session.use_trans_sid=1,經過1.php第一鏈接過去顯示不了session的值,但第二個鏈接則正常顯示。(說明url回寫正常運行)