session原理總結

weixin_34377065發表於2011-02-16

之前在學校的時候,只知道session與cookie的區別在於:session是儲存在伺服器端,cookie儲存在客戶端。session怎麼樣儲存的?以檔案的形式儲存。自己去測試過。有的忘記了。對應session的id號模糊不清。在開發中,非常有必要弄明白具體細節。不能停留在使用session_start()函式了,然後獲取session值。不知道里面的機制,在開發中遇到了新的問題,解決起來比較費心。

 

一、session是怎麼儲存的?怎麼去檢視其內容?

 

session是以檔案的形式儲存的。php.ini中有個配置項--session.save_path= "";這個裡面填寫的路徑,將會使session檔案儲存在該路徑下。session檔案的命名格式是:"sess_[PHPSESSID的值]"。每一個檔案,裡面儲存了一個會話的資料。其實只要使用程式碼$_SESSION['user_id'] = $value;就會促發php的session機制,結果往對應的session檔案中寫入一個值。

 

二、session.save_path路徑下這麼多的session檔案,php是如何確定要呼叫哪個session檔案的?

 

php是依據,一個名為PHPSESSID的cookie,根據它的值,確定要呼叫哪個session檔案的。去瀏覽器中,可以看到一個cookie名為PHPSESSID,假如它的值為"sess_adbjsf2q1ass26oootd163sf84",那麼,當訪問伺服器的時候,就會呼叫session目錄下名為"sess_adbjsf2q1ass26oootd163sf84"的檔案。其實,PHPSESSID就是一個會話id,以此來確定,哪個是你的會話資料。

以下是在瀏覽器檢視cookie所看到的

 

cookie的名字PHPSESSID是可以改的,在php.ini中 session.name = PHPSESSID就是設定該cookie的名字

 

 

結合自己實際開發中遇到的問題,總結一下:要說session跟cookie有關聯的地方,就是跟PHPSESSID這個cookie有繫結關係。其他,不管你設定什麼cookie,使用session的時候是不會用到這些值的。也無法獲取到。比如同步登陸,設定即使設定了cookie,而你的應用是依據session判斷是否為登陸狀態的(事實上也必須如此,因為session儲存在伺服器端,安全性更高,哪個依據cookie認為你已經登陸,那麼很慘)。

所以,這樣的情況就會出現,即使成功設定了cookie。也還是不能同步登陸。

 

 

三、經常遇到的現象:為什麼刪除一個session檔案,之後生成一個session檔案,新的檔名字還是與原來一樣?

 

 

理解到session檔案的命名規則是:“sess_PHPSESSID值“。那麼,就很容易明白了。因為,客戶端存在cookie:PHPSESSID。客戶端傳送請求後,會將該cookie傳送給伺服器(php可以使用$_COOKIE['PHPSESSID']看到其內容),這樣的話,還是會根據PHPSESSID生成一個session檔案的。

 

 

四、如何檢視session檔案中的session值?

 

 

我在開發中發現,如果僅僅依靠session_start()和$_SESSION['user_id']這樣的程式碼,去除錯,還不夠全面的瞭解問題所在。

 

比如,我想知道,session_start()到底在完成哪些操作?如果,想動態,實時知道session的值是如何被改寫的,開啟一個session檔案,檢視是很瞭然的。

 

原來,裡面就是儲存的是一些被序列化後的值。也明白一個知識點,"php聖經"中講解session的時候,提到session值做被序列化了。下面看到的session內容就是被序列化了。

開啟一個session檔案,內容如下:

cityID|i:0;cityName|s:3:"all";fanwe_lang|s:5:"zh-cn";fanwe_currency|a:4:{s:2:"id";s:1:"1";s:6:"name_1";s:9:"人民幣";s:4:"unit";s:3:"¥";s:5:"radio";s:6:"1.0000";}_fanwe_hash__|s:32:"77c18770c6cb5d89444c407aaa3e8477";

 

 

總結出讀取規則:

 

1、每一個session的值是以分號";"分開的。比如“cityID|i:0;cityName|s:3:"all";”就是一個完整的session值結束

 

2、裡面的讀取規則:符號“|”前面表示session名稱。符號後面是該session的具體資訊。包括:資料型別,字元長度,內容。上面第一個就相當於使用如下php程式碼訪問:$_SESSION['cityID']

 

後面的s表示資料值的長度,3表示字元長度。比如這一段:fanwe_lang|s:5:"zh-cn";  fanwe_lang是變數的名稱,變數值是"zh-cn",長度剛好是5,就是”s:5“標明的。

最後"all"就表示session的具體值了。就是使用程式碼$_SESSION['cityID']後會得到的結果。

 

3.一個session可以儲存一個陣列。符號{}表示陣列的內容。上面的花括號{}是$_SESSION['fanwe_currency']所儲存的內容。要想檢視id的值,就使用程式碼:$_SESSION['fanwe_currency']['id']

 

五、怎麼樣理解session_start等函式所做的實際操作是什麼?

 

 

我是這樣理解的:session_start,可以看成是建立一個session檔案。假如有原來的session檔案,或許沒有建立。引入一個。往session檔案中寫值,那是程式碼“$_SESSION['']=" ";  賦值所完成的操作。


session_start()生成一個新的session檔名時。會判斷是否存在cookie名為PHPSESSID的值。如果存在,那麼就會按照它的值,組合成一個檔名"sess_[phpcookie值]"。所以,在目錄下,老是能夠看到之前刪除過的session檔名。如果將瀏覽器中對應的cookie(PHPSESSID)刪掉。那麼就不會生成同樣的名字了。如果不存在名為PHPSESSID的cookie。php所做的估計為:先傳送一個cookie,然後按照cookie的值生成一個(我可以在瀏覽器中馬上看到一個名為PHPSESSID的cookie)

 

其實,現在也更加深刻地理解了一個知識:在呼叫session_start()之前不能有任何輸出。有輸出就會報錯。

 

session_start()已經封裝了傳送cookie的操作(傳送一個名稱為PHPSESSID的cookie到瀏覽器)。涉及到http的一個原理:頭部資訊必須在內容之前傳送才行。所以,使用echo '內容';

header('Content-type: text/xml; charset=gb2312');//頭部資訊,不算內容

 

 

可以這樣認為:session_start()內部已經進行了一次傳送頭部動作。所以之前不能有任何輸出內容。
手冊中的英文大致是這樣說的:建立一個session,或者恢復當前一個session(基於request請求傳遞的session id,這裡應該值的就是http請求時傳遞的名為PHPSESSID的cookie)

 

 

 

實際開發應用總結:

 

只要是同一個使用者的操作。導航程式訪問記錄和團購程式訪問的記錄都是儲存在同一個session檔案中

如果是不同的域呢?假如使用者訪問cs.test.com和daohang.test.com,兩方程式都設定了session。那麼session的結果儲存在同一個session檔案中嗎?

因為:伺服器是統一管理session檔案的存放的。而php引擎是根據phpsessionid的值確定要操作哪個session檔案。session

檔名的格式是:"sess_[phpcookie值]"。依次尋找對應的session檔案(於是在瀏覽器檢視名為PHPSESSIONID的cookie,過期時間是在會話結束後)
所以,只要cs.test.com和daohang.test.com使用的是同一臺伺服器。
這樣的話,假如是多臺伺服器的情況。那麼就不得不將session儲存在資料庫中去。這樣實現session共享。跟具體的伺服器是無關的。

 

(2013年更新:實際上共享session不侷限於資料庫中儲存,關於session共享方案,根據自己的理解一年後寫了一篇總結文章,http://www.cnblogs.com/wangtao_20/p/3395518.html



session檔案是某個使用者整個會話過程中資料。那麼,假如cs.test.com和daohang.test.com下的兩個程式執行在同一個伺服器上。就意味著,訪問cs.test.com與訪問daohang.test.com是同一個會話。也就意味著,這兩邊訪問後設定的session資料是儲存在同一個session檔案中的。

 

 

可以將名為PHPSESSIONID的cookie,其值看成是一個會話的id。會話結束後,該cookie過期或者被刪。那麼,伺服器對應的session檔案(名為"sess_[phpcookie值]")會被刪掉嗎?檢視發現並不會被刪掉。所以才會有session檔案很多,出現讀取效能的問題。session檔案比較多的情況下,產生I/Q讀寫效能問題。瞭解到可以將session檔案分多個目錄儲存(參考http://www.jb51.net/article/27941.htm)。php.ini中的配置項session.save_path,前面一個值M可以指定目錄的深度級別。這個沒測試過。需要用到的時候,再去測試一下。

 

 

待解決疑問

 

一、session的過期時間是怎麼確定的?


檢視session檔案內容,發現裡面有個值設定了session檔案的過期時間:__HTTP_Session_Expire_TS|i:1297750868;

已掌握的資訊:
PHPSESSID該cookie的過期時間在瀏覽器中顯示:會話結束後過期

所有的session檔案沒有被自動刪掉,只是有個過期時間,以此決定:是新生成一個session檔案還是使用原來的。

原來:伺服器定期session清理機制估計會用到這個東西

 

 

二、如果沒有設定php.ini中的引數。php預設會將session檔案儲存到什麼位置?

 

 

 

 

附網文:php.ini中配置session引數的說明。

【Session】

 

[服務端]

session.save_handler = files

預設為file,定義session在服務端的儲存方式,file意為把sesion儲存到一個臨時檔案裡,如果我們想自定義別的方式儲存

(比如用資料庫),則需要把該項設定為user;

 

session.save_path = "D:/APMServ5.2.0/PHP/sessiondata/"

定義服務端儲存session的臨時檔案的位置。

 

session.auto_start = 0

如置1,則不用在每個檔案裡寫session_start(); session自動start :)

 

session.gc_probability = 1

session.gc_divisor    = 100

session.gc_maxlifetime = 1440

這三個配置組合構建服務端session的垃圾回收機制

session.gc_probability與session.gc_divisor構成執行session清理的概率,理論上的解釋為服務端定期有一定的概率呼叫gc函式來對session進行清理,清理的概率為:gc_probability/gc_divisor 比如:1/100  表示每一個新會話初始化時,有

1%的概率會啟動垃圾回收程式,清理的標準為session.gc_maxlifetime定義的時間。

 

[客戶端]

session.use_cookies = 1

sessionid在客戶端採用的儲存方式,置1代表使用cookie記錄客戶端的sessionid,同時,$_COOKIE變數裡才會有$_COOKIE[

‘PHPSESSIONID’]這個元素存在;

 

session.use_only_cookies = 1

也是定義sessionid在客戶端採用的儲存方式,置1代表僅僅使用 cookie 來存放會話 ID。一般來說,現在客戶端都會支援

cookie,所以建議設定成1,這樣可以防止有關通過 URL 傳遞會話 ID 的攻擊。

 

session.use_trans_sid = 0

相對應於上面那個設定,這裡如果置1,則代表允許sessionid通過url引數傳遞,同理,建議設定成0;

 

session.referer_check = 

這個設定在session.use_trans_sid = 1的時候才會生效,目的是檢查HTTP頭中的"Referer"以判斷包含於URL中的會話id是否

有效,HTTP_REFERER必須包含這個引數指定的字串,否則URL中的會話id將被視為無效。所以一般預設為空,即不檢查。 

 

session.name = PHPSESSID

定義sessionid的名稱,即變數名,所以通過瀏覽器http工具看到的http標頭檔案裡的PHPSESSID=##############;

 

session.hash_function = 0

選擇session_name的加密方式,0代表md5加密,1代表sha1加密,預設是0,但是據說用sha1方式加密,安全性更高;

 

session.hash_bits_per_character = 4

指定在session_name字串中的每個字元內儲存多少位二進位制數,這些二進位制數是hash函式的運算結果。

4   bits:   0-9,   a-f 

5   bits:   0-9,   a-v 

6   bits:   0-9,   a-z,   A-Z,   "-",   ","

 

url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=,fieldset="

指定重寫哪些HTML標籤來包含sid(session_id)(僅在"session.use_trans_sid"開啟的情況下有效),URL重寫器將新增一個

隱藏的"<input>",它包含了本應當額外追加到URL上的資訊。 

 

session.cookie_lifetime = 0

儲存sessionid的cookie檔案的生命週期,如置0,代表會話結束,則sessionid就自動消失,常見的強行關閉瀏覽器,就會丟

失上一次的sessionid;

 

session.cookie_path = /

儲存sessionid的cookie檔案在客戶端的位置;

 

session.cookie_domain = /

儲存sessionid的cookie的域名設定,這跟cookie允許的域名的訪問許可權設定有關,一般來說想讓自己網站所有的目錄都能訪

問到客戶端的cookie,就應該設定成“/”如需要詳細瞭解,可以看下setcookie()函式的domain引數相關設定和使用方法;

 

session.bug_compat_42 = 1

session.bug_compat_warn = 1

這兩個可以說幾乎是快要被廢棄的設定,是為了老版本的php服務的,主要是針對 session_register函式,因為php5的

register_global預設是關閉狀態,所以在php5里根本用不到 session_register這個函式;並且php6就要廢除這個設定,直接定義為關閉,所以沒必要研究這兩個了;

 

2015.6.14

 

今天無意中看php手冊,注意到這句話:

 

在某些作業系統上,建議使用可以高效處理大量小尺寸檔案的檔案系統上的路徑來儲存會話資料。 例如,在 Linux 平臺上,對於會話資料儲存的工作而言,reiserfs 檔案系統會比 ext2fs 檔案系統能夠提供更好的效能。

這個是好的方向。session的資料一般很小。適合小檔案的檔案系統來使用,而我們的傳統的檔案系統不太適合。

 

 

相關文章