亚洲乱码卡一卡二卡三永久-亚洲乱码一二三四区-亚洲乱码一区二区三区在线观看-亚洲伦理一区-成人在色线视频在线观看免费大全-成人在线91

http協議是WEB服務器與客戶 端(瀏覽器)相互通信的協議,它是一種無狀態協議。所謂無狀態,指的是不會維護http請求數據,http請求是獨立的,非持久的。而越來越復雜的WEB 應用,需要保存一些用戶狀態信息。這時候,Session這種方案應需而生。PHP從4.1開始支持Session管理。

session是很抽象的一個概念。我們不妨先從與它幾個息息相關的有跡可尋的小切入點入手,然后逐漸地認識了解它。

session存儲

首先,我們為什么需要Session,就是因為我們需要存儲各個用戶的狀態數據。那么試問,如果由你來設計解決這個需求的方案,那么也許你會設置這樣一個數據表用與存儲各個用戶的狀態信息:
uid : 用戶唯一標識符,區分其它用戶

created : 記錄產生時間

data : 存放與用戶相關的數據

max_age : 記錄的有效時間

同樣地,PHP設計管理session方案也大致如此,它分別包含了以下信息:

1. session id
      用戶session唯一標識符,隨機生成的一串字符串,具有唯一性,隨機性。主要用于區分其它用戶的session數據。用戶第一次訪問web頁面的時候,php的session初始化函數調用會分配給當前來訪用戶一個唯一的ID,也稱之為session_id。

2. session data
      我們把需要通過session保存的用戶狀態信息,稱為用戶session數據,也稱為session數據。

3. session file
      PHP 默認將session數據存放在一個文件里。我們把存放session數據的文件稱為session文件。它由特殊的php.ini設置 session.save_path指定session文件的存放路徑,CentOS5.3操作系統,PHP5.1默認存放在/var/lib/php /session目錄中。用戶session文件的名稱,就是以sess_為前綴,以session_id為結尾命名,比如session id為vp8lfqnskjvsiilcp1c4l484d3,那么session文件名就是 sess_vp8lfqnskjvsiilcp1c4l484d3

4. session lifetime
我們把初始化session開始,直到注銷session這段期間,稱為session生命周期,這樣有助于我們理解session管理函數。

由 此,我們可見: 當每個用戶訪問web, PHP的session初始化函數都會給當前來訪用戶分配一個唯一的session ID。并且在session生命周期結束的時候,將用戶在此周期產生的session數據持久到session文件中。用戶再次訪問的時 候,session初始化函數,又會從session文件中讀取session數據,開始新的session生命周期。

與session存儲相關php.ini設置

1. session.save_handler = file
用于讀取/回寫session數據的方式,默認是files。它會讓PHP的session管理函數使用指定的文本文件存儲session數據

2. session.save_path =“/var/lib/php/session”
指 定保存session文件的目錄,可以指定到別的目錄,但是指定目錄必須要有httpd守護進程屬主(比如apache或www等)寫權限,否則無法回存 session數據。當指定目錄不存在時,php session環境初始化函數是不會幫你創建指定目錄的,所以需要你手工建立指定目錄。
它還可以寫成這樣session.save_path =“N;/path” 其中N是整數。這樣使得不是所有的session文件都保存在同一個目錄中,而是分散在不同目錄。這對于服務器處理大量session文件是很有幫助的。(注:目錄需要自己手工創建)

3. session.auto_start = 0
如果啟用該選項,用戶的每次請求都會初始化session。我們推薦不啟用該設置,最好通過session_start()顯示地初始化session。

Session同步數據

一 旦調用了session_start()初始化session,就意味著開始了一個session生命周期。也就是宣布了,可以使用相關函數操 作$_SESSION來管理session數據。這個session生命周期產生的數據并沒有實時地寫入session文件,而是通過$_SESSION 變量寄存在內存中。那么,寄存在內存的數據什么時候會寫入到session文件?這也是我們這一小節的主要測試內容。

在進行測試之前,先讓我們介紹幾個影響session數據的PHP函數、或事件

1. session_start()
      函數session_start會初始化session,也標識著session生命周期的開始。要使用session,必須初始化一個session環境。有點類似于OOP概念中調用構造函數構創建對象實例一樣。
session 初始化操作,聲明一個全局數組$_SESSION,映射寄存在內存的session數據。如果session文件已經存在,并且保存有session數 據,session_start()則會讀取session數據,填入$_SESSION中,開始一個新的session生命周期。

2. $_SESSION
      它是一個全局變量,類型是Array,映射了session生命周期的session數據,寄存在內存中。在session初始化的時候,從session文件中讀取數據,填入該變量中。在session生命周期結束時,將$_SESSION數據寫回session文件。

3. session_register()
      在 session生命周期內,使用全局變量名稱將注全局變量注冊到當前session中。所謂注冊,就是將變量填入$_SESSION中,值為NULL。它 不會對session文件進行任何IO操作,只是影響$_SESSION變量。注意,它的正確寫法是 session_register(‘varname’),而不是session_register($varname)

4. session_unregister()
      與session_register操作正好相反,即在session生命周期,從當前session注銷指定變量。同樣只影響$_SESSION,并不進行任何IO操作。

5. session_unset()
      在 session生命周期,從當前session中注銷全部session數據,讓$_SESSION成為一個空數組。它與 unset($_SESSION)的區別在于:unset直接刪除$_SESSION變量,釋放內存資源;另一個區別在 于,session_unset()僅在session生命周期能夠操作$_SESSION數組,而unset()則在整個頁面(page)生命周期都能 操作$_SESSION數組。session_unset()同樣不進行任何IO操作,只影響$_SESSION數組。

6. session_destroy()
如果說session_start()初始化一個session的話,而它則注銷一個session。意味著session生命周期結束了。在 session生命周期結整后,session_register, session_unset, session_register都將不能操作$_SESSION數組,而$_SESSION數組依然可以被unset()等函數操作。這 時,session意味著是未定義的,而$_SESSION依然是一個全局變量,他們脫離了關映射關系。
通過session_destroy()注銷session,除了結束session生命周期外,它還會刪除sesion文件,但不會影響當前$_SESSION變量。即它會產生一個IO操作。

7. session_regenerate_id()
      調 用它,會給當前用戶重新分配一個新的session id。并且在結束當前頁面生命周期的時候,將當前session數據寫入session文件。前提是,調用此函數之前,當前session生命周期沒有被 終止(參考第9點)。它會產生一個IO操作,創建一個新的session文件,創建新的session文件的是在session結束之前,而不是調用此函 數就立即創建新的session文件。

8. session_commit()
      session_commit() 函數是session_write_close()函數的別名。它會結束當前session的生命周期,并且將session數據立即強制寫入 session文件。不推薦通過session_commit()來手工寫入session數據,因為PHP會在頁面生命周期結束的時候,自動結束當前沒 有終止的session生命周期。它會產生一個IO寫操作

9. end session
      結 束session,默認是在頁面生命周期結束的之前,PHP會自動結束當前沒有終止的session。但是還可以通過session_commit()與 session_destroy()二個函數提前結束session。不管是哪種方式,結束session都會產生IO操作,分別不一樣。默認情況,產生 一個IO寫操作,將當前session數據寫回session文件。session_commit()則是調用該函數那刻,產生一個IO寫操作,將 session數據寫回session文件。而session_destroy()不一樣在于,它不會將數據寫回session文件,而是直接刪除當前 session文件。有趣的是,不管是session_commit(),還是session_destroy()都不會清空$_SESSION數組,更 不會刪除$_SESSION數組,只是所有session_*函數不能再操作session數據,因為當前的session生命周期終止了,即不能操作一 個未定義對象。

為了驗證以上陳述,我們可以做以下測試
      任務1: 觀察session初始化與默認結束session的時候,產生的IO操作

[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
      藍色加粗,通過系統內核函數open調用打開session文件,這是由session_start()產生的調用,
      注意這里并沒有產生讀文件操作。紅色部分,將一個空字符串寫入session文件。
      由此可見session初始化在頁面生命周期開始之時,手工調用session_start可以初始化session文件,
      而在頁面生命周期結束之時,會自動地注銷session,結束當前session生命周期,
      同時在此周期產生的session數據寫回session文件,我們把這種方式結束的session,稱為session默認結束。 

任務2 觀察session_register()查看它是否會產生磁盤操作,還是只操作$_SESSION。

 

通 過上面的觀察,藍色部分還是由session初始化(session_start)產生,注意這里依然沒讀文件操作,這是因為session文件為空。紅 色部分,依然是默認結束session產生的文件寫操作(pwrite)。由此,我們可以知道session_register()不會對session 文件操作,即不會把$_SESSION中的數據寫回session文件,它沒有產生任何IO操作。而只在session生命周期是影響當 前$_SESSION變量,即$_SESSION[‘pg_uuid’] = NULL。所以,推薦使用$_SESSION[‘pg_uuid’] = $pg_uuid;

任務3 觀察session_destroy()與session_unset()的區別

   

 

//@這里是頁面析構的時候– 本應該將$_SESSION數據同步到session文件, 真的嗎???
//@事實,沒有發生任何IO操作,即沒有將$_SESSION數據回寫,怎么回事???
//@因為被session_destroy()消毀了session…
 
程序輸出:
   

 

[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
 

藍 色部分是我們熟悉的session初始化的時候產生的open系統內核調用。綠色部分,是一個IO讀操作,因為上一次訪問頁面的時候,產生了 session數據,所以這一次會將上次的session填入$_SESSION中。紅色部分,可以看出,這里調用unlink刪除session文件, 而且后面(頁面生命周期結束時),一直沒有看到前兩例看到的任何與session文件有關的IO寫操作,即沒有將$_SESSION中的數據寫回 session文件。我們也沒有在session.save_path找到相應的session文件

[root@localhost html]# ls /var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5  ls: /var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5: No such file or directory

注意: 雖然刪除了session文件,但用戶再次訪問web的時候,并不會給用戶重新分配一個新的session id,而是依然用該session id,并且會重新創建文件名相同的session文件,即sess_SESSION-ID

任務4測試并觀察session_regenerate_id行為,以及$_SESSION的變化

觀 察測試結果,藍色部分照舊是session初始化的時候產生的系統內核open調用,接著綠色部分是一個IO讀操作,即讀取session文件中的數據, 由第一個var_dump($_SESSION)輸出。隨后,往session加入新的一條已定義了的session記錄,并且通過 session_commit()將記錄寫回去。紅色部分就是由session_commit產生的一次IO寫操作。之 后,session_unset()并沒有生效,同時,我們也沒有在頁面生命周期結束的時候看到任何與session文件有關的IO寫操作。這也正說明 了,session_commit()調用的當下,就會將session數據寫回session文件,并且會像session_destroy一樣注銷 session,但與session_destroy不同的時,session_commit不會刪除session文件,而且會將當前的session 數據寫回session文件。我們可以查看,調用session_commit之后,session文件還是依然存在的

[root@localhost html]# ls -lt /var/lib/php/session 
 -rw------- 1 apache apache 31 Apr 11 03:18 sess_qoa6knu9fg77un8le99o1vk1c7  
 -rw------- 1 apache apache 11 Apr 11 00:08 sess_4j38nv7l1fq1bj6n80l6g9cum5  …


總結:

1, 用戶注銷web應用系統,最好的調用方式依次是 session_unset();  session_destroy();  unset($_SESSION); 
2, 盡量將鍵與值填入$_SESSION,而不推薦使用session_register()。同樣,盡量使用unset($_SESSION[‘var’]),而不使用session_unregister()。

3, 對于可能產生大量session的WEB應用,推薦使用的session.save_path的格式是session.save_path=”N:/path”。注意:這些目錄需要手工創建,并且有httpd守護進程屬主寫權限。這樣做可以獲得更好的性能

4, 如果調用了session_regenerate_id()給用戶分配了新的session id。該函數并不會主動刪除舊的session文件,需要定時清理舊的session文件,這樣更優化。

5, 盡量不要使用session_commit()提交sessioin數據,因為它同時會結束當前session,PHP默認會在頁面生命周期的時候提交session數據到session文件

Session ID傳遞

session 終究是因為管理用戶狀態信息才存在的。我們曾探討過session id的意義:每個來訪問用戶都會被分配一個唯一的session id,用于區分其它用戶的session數據。換句話說,session id是用戶表明身份的一種標識,就像入場券一樣。用戶一旦從被分配了session id之后的每次訪問(http請求)都會攜帶這個session id給服務端,用于加載該用戶的session數據。那么,通過什么方式傳給服務端?這是我們這節探討的內容。

用 戶端與服務端的web通信協議是http。而PHP通過http取得用戶數據慣用的三種方法分別是:POST方法、GET方法還有Cookie。而PHP 默認傳遞方法正是Cookie,也是最佳方法。只有在客戶端不支持Cookie的時候(瀏覽器禁用了Cookie功能)才會通過GET方法來傳遞 session_id,即通過在URL的query_string部分傳遞session id。

確 定了傳遞方法,我們還有必要清楚一下session id的傳遞過程。用戶通過瀏覽器訪問網頁,將URL輸入地址欄回車,瀏覽器發出請求,在調用sockect send之前瀏覽器引擎會搜索有效的Cookies記錄封裝在http請求頭的Cookie字段,一同發送出去。服務端器接收到請求后,交給PHP處理。 這時,session初始化函數如果在$_COOKIE中沒有找到以session_name()作為鍵值存儲的生素(值為session id),則會以為用戶是第一次訪問web。作為第一次訪問的用戶,session初始化函數總會隨機生成一個session_id并且通過 setcookie()函數調用將新生成的session_id以”sesseson_name = session_id”的格式填入http響應頭Set-Cookie字段,發送給客戶端(這樣接下來的請求,http請求頭Cookie字段都會攜帶該 Cookie記錄給web服務器)。如果初始化函數發現用戶端Cookies中已定義了存在$_COOKIE[‘sess_name’],則會加載 與$_COOKIE[‘sess_name’]相對應的session文件($_COOKIE[‘sess_name’]就是session ID)。如果用戶Cookie記錄過期,則會被瀏覽器刪除。之后的下一次請求,服務器會以為用戶又是第一次訪問,如此循環。

讓我們通過測與來驗證以上的陳述

第 一次訪問/a.php的時候,請求包里面沒有設置任何Cookie,所以這里的Cookie字段為空。當然服務器php也就得不到 的$_COOKIE[‘PHPSESSID’](即session id為空)。如此,服務器會以為用戶是第一次訪問web。所以session初始化的時候,會給用戶分配一個唯一的session_id并且以 Cookie的方法傳回給了用戶端。

我們再來觀察第二次請求與響應,會有哪些變化:

首 先,我們觀察http請求,加紅色部分是第一次http請求頭沒有出現的內容。我們可以看到,該Cookie正是第一次訪問,服務端通過Set- Cookie要求瀏覽器設置的Cookie。它們是一樣的,即session_id為bk7655dqrm5m884c9nitfi7j00。然后,我們 再觀察這次的http響應,明顯沒有再要求用戶端設置鍵為session_name()的Cookie了。

我們再來測試偽造一個session_id發送給服務,觀察服務端響應。我們寫一個測試腳本,如下:

抓到的http請求、響應數據包如下:

      上面的session_id是用戶端偽造的一個值,它并不實際存在。收到這樣的請求,服務端并沒有檢查,而是以這個session_id創建了相應的 session文件。并且,從httpd響應頭部信息來看,并沒給用戶端分配session id(沒有Set-Cookie)。由此,我們可以推斷:只要http請求頭部包含了以session_name()作為鍵值的Cookie,那么服務端 就不認為用戶是第一次訪問web,亦不會給客戶端分配session_id。否則,分配新的session_id,并通過Set-Cookie要求瀏覽器 創建該Cookie.
我們再來觀察一下,通過session_regenerate_id()函數給用戶分配一個新的session_id的情況

##
上 面可以觀察得到,http請求頭Cookie部分帶了session id,并且這個session_id還是用戶第一次訪問web時被分配得到的。這一次,http響應頭跟第二次示例http響應有些不一樣,而是像第一次 訪問那樣通過Set-Cookie去要求用戶端瀏覽器更新用戶的session id。這意味著:session_genrate_id()給用戶端重新生成的session id也是通過Cookie的方法傳遞。

1,User01和User02第一次去訪問/p1.php,分別被分配了一個session id。

2,User01和User02第二次訪問web,都會使用由/p1.php分配的session_id

3,User01 因為訪問了/p2.php,腳本/p2.php中的session_regenerate_id()給用戶User01重新分配了一個新 session_id,從用戶User01第4次訪問的session_id就可以看得出來,與前面幾幾次的session_id不同了。

4,User02因為沒有訪問/p2.php,也就沒有被服務端重新分配session id,一下沿用著上一次分配的session_id與session id傳遞的有關的php.ini設置

1,session.use_cookie = 1
是否采用Cookie方法傳遞session id值。默認是1,表示啟用。

2,session.name = PHPSESSID
不 管是Cookie傳遞sessioin_id,還是GET方法傳遞session_id,都需要使用鍵值。他們的格式分別是Cookie:  sess_name=session_id;和/path.php?sess_name=session_id,其中sess_name就是由這里指定的

3,session.use_only_cookies = 0
表 示只使用Cookie 的方法傳遞session id。我們說過,傳遞cookie的方法,除了cookie,還有GET方法,GET方法是不安全的方法。在用戶端禁用了cookie的時候,會采用 GET方法傳遞session_id,可以通過這個設置盡用GET方法傳遞session_id。

4,session.cookie_lifetime = 0, session.cookie_path = / 以及session.cookie_domain =
如 果使用Cookie方法傳遞session_id的話,這里分別指定了cookie有效域、目錄和時間。分別對應setcookie()函數的形 參$expire、$path和$domain。其中cookie_lifetime=0表示直到關閉瀏覽器才刪除Cookie。還可以使用 session_set_cookie_params()函數修改這些值。

5,session_name([string $name])
獲 取或更新session_name。如果傳了name,則表示不使用默認的名稱PHPSESSID(由session.name)指定,否則獲取當前 session_name。注意:如果設置session_name,則必須在session_start()之前調用才生效。

6,session_id([string $id])
與session_name()類似,但它是讀取或者設置session_id的方法。同樣,設置session_id的話,必須在session_start()之前調用才有效。

7,session_set_cookie_params()和session_get_cookie_params()
通 過session_set_cookie_params()可以重新設定session.cookie_lifetime, session.cookie_path以及session.cookie_domain這三個php.ini設置。而 session_get_cookie_params()則是獲取這些設定的值。

Session回收

通過上文幾節介紹,我們知道session數據存放在服務端指定的session.save_path目錄中,同時會在用戶端存放一條Cookie用以記錄分配給用戶的session id。所以,session數據失效分服務端和客戶端,要刪除(回收)的對象也很清楚:
1,服務端:刪除過期的session文件,啟動PHP GC回收。
2,用戶端:使存儲了過期session_id的用戶端Cookie記錄過期。通過將Cookie的Expire設置為負值,要求客戶端刪除Cookie。
服務端:刪除過期的session文件
PHP GC進程被啟動以后,則會掃描session.save_path,找出過期的session,并刪除該session文件。所謂,過期的 session,是指操作系統當前時間與session文件最后訪問時間之差大于session.gc_maxlifetime的話,該session認 為是過期了。注意:有時候,你會發現,即便是文件過期了,有可能也沒有被及時地刪除掉。這是因為,每次session初始化的時候,并不會都啟動PHP GC進程的,啟動GC進程會大大降低php的運行效率。所有一個啟動概率,這個概率由php.ini設定session.gc_probability / session.gc_divisor二個設置決定,默認概率是1%(1/1000)。這意味著,每1000次用戶請求中,會啟動1次PHP GC回收session文件。比如,我們下面看到的,過期的session文件依然存在:

# date;find /var/lib/php/session -type f -atime -1440 -print |xargs ls -lt  
-rw------- 1 apache apache  0 Apr 12 20:01 /var/lib/php/session/sess_5tlaq5a8im3ob1bikn62motpv7  
-rw------- 1 apache apache  0 Apr 12 19:39 /var/lib/php/session/sess_f7q6jfdug4ekfsjhop6jftgna7  
-rw------- 1 apache apache  0 Apr 12 17:03 /var/lib/php/session/sess_dk7655dqrm5m884c9nitfi7j00

我們可以通過編輯設置,來驗證啟動php session的GC機制

從 上面藍色部分可以看出,通過用stat64檢查session文件的狀態,如果發現過期了,則會通過調用系統內核函數ulink()刪除過期的 session文件。可見,session初始化的時候會啟動GC, GC會掃描session.save_path中的所有session文件,查看他們狀態并且將過期的文件刪除。正因為如此,所以默認設置啟動的概率是 1/1000。

客戶端:刪除過期session id的cookie記錄
如 果用戶發現session已經過期,但是服務端的GC還沒有啟動,服務端可以手通過手工代碼setcookie的方式要求用戶端瀏覽器刪除鍵值為 session_name()的Cookie記錄。這樣,下回訪問的時候,瀏覽器以為用戶是第一次訪問,并且重新給訪問用戶分配一個新的 session_id。較好的做法類似這樣:

   
上面觀察可以知道,通過訪問/session_destroy.php,它要求客戶端將session_id的Cookie記錄刪除。而接下來訪問 /p1.php的時候,http請求頭沒有通過Cookie將用戶的session id帶給服務器(因為剛被要求刪除)。而第二次請求/p1.php的http響應里頭可以看到,服務端又給用戶重新分配了一個新的session id,而且不會繼續使用過去的session數據。

與session回收相關的php.ini設置:

1, session.gc_probability和session.gc_divisor
由 這二個函數決定了啟用GC的概率,默認是1/1000。也就是說,每一千次用戶請求中有一次會啟動GC回收session。啟動GC進程不宜過于頻繁。上 面的例子,我們可以看到,它會每次檢查session.save_path目錄下每個文件的狀態。這樣會降低php的執行效率。

2, session.gc_maxlifetime = 1440
設置session存活時間,單位是秒。每次GC啟動后, 會通過stat得到session文件最后訪問的unix時間,通過現在時間減去文件最后訪問時間之間大于session.gc_maxlifetime,則會刪除該文件。

總結

1, PHP使用Cookie的方法傳遞session id。盡量不要使用GET方法傳遞session id,因為這樣很不安全。

2, 可以通過setcookie()的方法,將客戶端的session id的Cookie記錄刪除。

3, PHP GC進程由session初始化啟動。但不是每一次用戶請求都會被啟動,它的啟動概率默認是1/1000。過于頻繁訪問的網站,并發量大的網站,可減小PHP GC的啟動頻率。PHP GC回收session會降低php的執行效率。

4, 通過下面代碼,優化session回收

 

session_start();
if (isset($_SESSION['SESS_TIMEOUT'])) {
  if ($_SERVER['REQUEST_TIME'] > $_SESSION['SESS_TIMEOUT']) {
    setcookie(session_name(), session_id(), -1, ‘/’);
    session_unset();
    session_destroy();
  }
} else {
  $_SESSION['SESS_TIMEOUT'] = $_SERVER['REQUEST_TIME'] + 3600;
}

穩定

產品高可用性高并發

貼心

項目群及時溝通

專業

產品經理1v1支持

快速

MVP模式小步快跑

承諾

我們選擇聲譽

堅持

10年專注高端品質開發
  • 返回頂部
主站蜘蛛池模板: 黑人极品videos精品欧美裸 | 日韩免费在线播放 | 一级片网| 2021天天躁夜夜燥西西 | 精品国产综合成人亚洲区 | 直接观看黄网站免费视频 | 成人黄色一级视频 | 欧美午夜性刺激在线观看免费 | 亚洲成a人片在线观看中文!!! | 国产一区二区在线观看视频 | 国产成人综合95精品视频免费 | 亚洲 欧洲 日产 韩国在线 | 欧美在线区 | 国产一级在线 | 一级黄色在线观看 | 亚洲一二四区性毛片1在线 亚洲一二三在线 | 国产三级日本三级在线播放 | 色成人综合网 | 图片区 日韩 欧美 亚洲 | 日本人欧美xx| 国产欧美日韩在线 | 波多野结衣中文字幕一区二区三区 | 日批视频网址免费观看 | 国内精品久久久久久网站 | 人与鲁牲交持级毛片 | 亚洲精品香蕉婷婷在线观看 | 一区二区午夜 | 国产成人综合亚洲亚洲欧美 | 成年人网站在线观看免费 | 亚洲天天做日日做天天看2018 | 99热免费在线观看 | 亚洲日韩中文第一精品 | 日韩在线专区 | 搜索毛片| 不卡一区二区在线观看 | 欧美xxxxxxx人妖极品 | 国产在线精品一区二区中文 | 521色香蕉网在线观看免费 | 午夜男人一级毛片免费 | 性夜影院午夜看片 | 国产成人精品一区二区不卡 |