文档章节

【开发必读】PHP session详解

PHP开发学习门户
 PHP开发学习门户
发布于 2014/09/18 18:03
字数 4232
阅读 32
收藏 0

个人原创地址:  http://www.phpthinking.com/archives/318   

重要提示:本站有字数限制,未完全显示,欢迎访问原创地址

个人原创地址:  http://www.phpthinking.com/archives/318   


http协议是WEB服务器与客户端(浏览器)相互通信的协议,它是一种无状态协议。所谓无状态,指的是不会维护http请求数据,http请求是独立的,非持久的。而越来越复杂的WEB应用,需要保存一些用户状态信息。这时候,Session这种方案应需而生。PHP从4.1开始支持Session管理。

session是很抽象的一个概念。我们不妨先从与它几个息息相关的有迹可寻的小切入点入手,然后逐渐地认识了解它。
session存储
首先,我们为什么需要Session,就是因为我们需要存储各个用户的状态数据。那么试问,如果由你来设计解决这个需求的方案,那么也许你会设置这样一个数据表用与存储各个用户的状态信息:
uid created data max_age
94c55770fdf044a7 1270802787 jtUsername=admin 14400
2c37df64277e4409 1270822787 jtUsername=Joe;jtBooks=8; 14400

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操作

1 <?php
2 //@file  test_session_2.php
3 session_start();
4 $pg_uuid 'ac606826-9620-490b-b850-ea9dbce6cfd5';
5
6 //注册全局变量pg_uuid到session,但$_SESSION['pg_uuid']值为NULL,只影响$_SESSION
7 session_register('pg_uuid');
8 var_dump($_SESSION);
9 fopen(__FILE__"r");

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

01 Process 21819 attached - interrupt to quit
02 ...
03 st_mode=S_IFREG|0644, st_size=72, ...}) = 0
04 open("/var/www/html/test_session.php", O_RDONLY) = 17
05 fstat64(17, {st_mode=S_IFREG|0644, st_size=72, ...}) = 0
06 lseek(17, 0, SEEK_CUR) = 0
07 read(17, "<p;?php\n//@file  test_session.php\ns"..., 8192) = 72
08 read(17, "", 8192) = 0
09 read(17, "", 8192) = 0
10 close(17) = 0
11 gettimeofday({1270906664, 11602}, NULL) = 0
12 open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
13 flock(17, LOCK_EX) = 0
14 fcntl64(17, F_SETFD, FD_CLOEXEC) = 0
15 fstat64(17, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
16 time(NULL) = 1270906664
17 open("/var/www/html/test_session.php", O_RDONLY) = 18
18 fstat64(18, {st_mode=S_IFREG|0644, st_size=72, ...}) = 0
19 lseek(18, 0, SEEK_CUR) = 0
20 close(18) = 0
21 chdir("/var/lib/php/session") = 0
22 pwrite64(17, "", 0, 0) = 0
23 close(17) = 0
24 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
25 writev(16, [{"HTTP/1.1 200 OK\r\nDate: Sat, 10 A"..., 385}], 1) = 385
26 write(12, "192.168.0.98 - - [10/Apr/2010:21"..., 207) = 207
27 shutdown(16, 1 /* send */) = 0
28 epoll_wait(15,

蓝色加粗,通过系统内核函数open调用打开session文件,这是由session_start()产生的调用,
注意这里并没有产生读文件操作。红色部分,将一个空字符串写入session文件。
由此可见session初始化在页面生命周期开始之时,手工调用session_start可以初始化session文件,
而在页面生命周期结束之时,会自动地注销session,结束当前session生命周期,
同时在此周期产生的session数据写回session文件,我们把这种方式结束的session,称为session默认结束。

任务2: 观察session_register()查看它是否会产生磁盘操作,还是只操作$_SESSION。

1 <?php
2 //@file  test_session_2.php
3 session_start();
4 $pg_uuid 'ac606826-9620-490b-b850-ea9dbce6cfd5';
5 session_register('pg_uuid'); //注册全局变量pg_uuid到session,但值为NULL,只影响$_SESSION
6 var_dump($_SESSION);
7 fopen(__FILE__"r");

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

01 Process 21819 attached - interrupt to quit
02 ...
03 open("/var/www/html/test_session_2.php", O_RDONLY) = 17
04 fstat64(17, {st_mode=S_IFREG|0644, st_size=148, ...}) = 0
05 lseek(17, 0, SEEK_CUR) = 0
06 read(17, "<?php\nsession_start();\n$pg_uuid "..., 8192) = 148
07 read(17, "", 8192) = 0
08 read(17, "", 8192) = 0
09 close(17) = 0
10 open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
11 flock(17, LOCK_EX) = 0
12 fcntl64(17, F_SETFD, FD_CLOEXEC) = 0
13 fstat64(17, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
14 time(NULL) = 1270907613
15 open("/var/www/html/test_session_2.php", O_RDONLY) = 18
16 fstat64(18, {st_mode=S_IFREG|0644, st_size=148, ...}) = 0
17 lseek(18, 0, SEEK_CUR) = 0
18 close(18) = 0
19 chdir("/var/lib/php/session") = 0
20 pwrite64(17, "pg_uuid|N;", 10, 0) = 10
21 close(17) = 0
22 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
23 writev(16, [{"HTTP/1.1 200 OK\r\nDate: Sat, 10 A"..., 328}, {"array(1) {\n [\"pg_uuid\"]=>\n NUL"..., 36}], 2) = 364
24 write(12, "192.168.0.98 - - [10/Apr/2010:21"..., 210) = 210
25 shutdown(16, 1 /* send */) = 0
26 epoll_wait(15,

通过上面的观察,蓝色部分还是由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()的区别

01 <?php
02 session_start();
03 echo "<br/>---1--<br/>";
04 $pg_uid = 1;
05 //$_SESSION['pg_uid']; //该行会报一个Notice消息,即没有初始化该变量
06 $_SESSION['pg_name'] = 'boys'//填入到$_SESSION变量,但不立即写入session文件,值为boys
07 $pg_sex = 1;
08 $pg_theme 'default';
09 session_register('pg_sex'); //填入到$_SESSION变量,但不立即写入session文件,值为NULL
10 session_register('pg_theme'); //填入到$_SESSION变量,但不立即写入session文件,值为NULL
11 var_dump($_SESSION);
12
13 //--
14 echo "<br/>---2--<br/>";
15 unset($_SESSION['pg_theme']); //从$_SESSION清除该元素,不立即同步到session文件
16 unset($_SESSION['pg_name']); //从$_SESSION清除该元素,不立即同步到session文件
17 session_unregister('pg_sex'); //从$_SESSION清除该元素,不立即同步到session文件
18 session_unregister('pg_uid'); //从$_SESSION清除该元素,不立即同步到session文件
19 var_dump($_SESSION);
20
21 echo "<br/>---3--<br/>";
22 $_SESSION['pg_members'] = 5; //填入$_SESSION数组,但不立即同步到session文件,值为5
23 $pg_boy = 6;
24 session_register('pg_boy'); //填入$_SESSION数组,但不立即同步到session文件,值为NULL
25 session_unset($_SESSION); //清空$_SESSION
26 var_dump($_SESSION);
27
28 echo "<br/>---4--<br/>";
29 $_SESSION['pg_boss'] = 3; //填入$_SESSION数组,但不立即同步到session文件,值为3
30 $pg_girls = 6;
31 session_register('pg_girls'); //填入$_session数组,但不立即同步到session文件,值为NULL
32 session_destroy(); //注销session_destroy
33 var_dump($_SESSION);
34
35 echo "<br/>---5---<br/>";
36 session_unregister('pg_boss'); //pg_boss不会被清除,还为NULL
37 session_unset(); //不会清空$_SESSION数组,因为session已被session_destroy注销
38 var_dump($_SESSION);
39
40 fopen(__FILE__"r");
41
42 //@这里是页面析构的时候-- 本应该将$_SESSION数据同步到session文件, 真的吗???
43 //@事实,没有发生任何IO操作,即没有将$_SESSION数据回写,怎么回事???
44 //@因为被session_destroy()消毁了session...
45 [php]
46 程序输出:
47 [php]
48 ---1--
49 array(3) { ["pg_name"]=> string(4) "boys" ["pg_sex"]=> NULL ["pg_theme"]=> NULL }
50 ---2--
51 array(0) { }
52 ---3--
53 array(0) { }
54 ---4--
55 array(2) { ["pg_boss"]=> int(3) ["pg_girls"]=> NULL }
56 ---5---
57 array(2) { ["pg_boss"]=> int(3) ["pg_girls"]=> NULL }

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

01 Process 21819 attached - interrupt to quit
02 ...
03 open("/var/www/html/test_session_3.php", O_RDONLY) = 17
04 fstat64(17, {st_mode=S_IFREG|0644, st_size=706, ...}) = 0
05 lseek(17, 0, SEEK_CUR) = 0
06 read(17, "<?php\nsession_start();\necho \"<br"..., 8192) = 706
07 read(17, "", 8192) = 0
08 read(17, "", 8192) = 0
09 close(17) = 0
10 open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
11 flock(17, LOCK_EX) = 0
12 fcntl64(17, F_SETFD, FD_CLOEXEC) = 0
13 fstat64(17, {st_mode=S_IFREG|0600, st_size=10, ...}) = 0
14 pread64(17, "pg_uuid|N;", 10, 0) = 10
15 close(17) = 0
16 unlink("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5") = 0
17 time(NULL) = 1270910665
18 open("/var/www/html/test_session_3.php", O_RDONLY) = 17
19 fstat64(17, {st_mode=S_IFREG|0644, st_size=706, ...}) = 0
20 lseek(17, 0, SEEK_CUR) = 0
21 close(17) = 0
22 chdir("/var/lib/php/session") = 0
23 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
24 ...
25 write(12, "192.168.0.98 - - [10/Apr/2010:22"..., 211) = 211
26 shutdown(16, 1 /* send */) = 0

蓝色部分是我们熟悉的session初始化的时候产生的open系统内核调用。绿色部分,是一个IO读操作,因为上一次访问页面的时候,产生了session数据,所以这一次会将上次的session填入$_SESSION中。红色部分,可以看出,这里调用unlink删除session文件,而且后面(页面生命周期结束时),一直没有看到前两例看到的任何与session文件有关的IO写操作,即没有将$_SESSION中的数据写回session文件。我们也没有在session.save_path找到相应的session文件

1 [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的变化

1 <?php
2 session_start();
3 $_SESSION['pfid'] = 123;
4 var_dump($_SESSION);
5 session_regenerate_id();
6 var_dump($_SESSION);
7 fopen(__FILE__"r");

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

01 Process 22641 attached - interrupt to quit
02 ...
03 open("/var/www/html/test_session_4.php", O_RDONLY) = 17
04 fstat64(17, {st_mode=S_IFREG|0644, st_size=141, ...}) = 0
05 lseek(17, 0, SEEK_CUR) = 0
06 read(17, "<?php\nsession_start();\n$_SESSION"..., 8192) = 141
07 read(17, "", 8192) = 0
08 read(17, "", 8192) = 0
09 close(17) = 0
10 open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
11 flock(17, LOCK_EX) = 0
12 fcntl64(17, F_SETFD, FD_CLOEXEC) = 0
13 fstat64(17, {st_mode=S_IFREG|0600, st_size=11, ...}) = 0
14 pread64(17, "pfid|i:123;", 11, 0) = 11
15 gettimeofday({1270915896, 122016}, NULL) = 0
16 time(NULL) = 1270915896
17 open("/var/www/html/test_session_4.php", O_RDONLY) = 18
18 fstat64(18, {st_mode=S_IFREG|0644, st_size=141, ...}) = 0
19 lseek(18, 0, SEEK_CUR) = 0
20 close(18) = 0
21 chdir("/var/lib/php/session") = 0
22 close(17) = 0
23 open("/var/lib/php/session/sess_qoa6knu9fg77un8le99o1vk1c7", O_RDWR|O_CREAT, 0600) = 17
24 flock(17, LOCK_EX) = 0
25 fcntl64(17, F_SETFD, FD_CLOEXEC) = 0
26 pwrite64(17, "pfid|i:123;", 11, 0) = 11
27 close(17) = 0
28 setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
29 writev(16, [{"HTTP/1.1 200 OK\r\nDate: Sat, 10 A"..., 386}, {"array(1) {\n [\"pfid\"]=>\n int(12"..., 75}], 2) = 461
30 write(12, "192.168.0.98 - - [11/Apr/2010:00"..., 210) = 210
31 shutdown(16, 1 /* send */) = 0


© 著作权归作者所有

上一篇: MySQL乱码问题
PHP开发学习门户
粉丝 11
博文 39
码字总数 39233
作品 0
海淀
私信 提问
加载中

评论(1)

PHP开发学习门户
PHP开发学习门户 博主
文章字数限制,未完全显示 请访问原地址
PHPthinking的发展

我是一名大四学生,就在几个月前我成为了一名PHP工程师,工作之余,两个人没日没夜的做了个PHP开发学习门户网站,用来分享自己人学习和开发PHP程序中遇到的问题,以及一些优秀的文章,又或者...

PHP开发学习门户
2014/09/23
2
0
PHP _ session 详解

http协议是WEB服务器与客户端(浏览器)相互通信的协议,它是一种无状态协议。所谓无状态,指的是不会维护http请求数据,http请求是独立的,非持久的。而越来越复杂的WEB应用,需要保存一些用户...

喵王不瞌睡
2015/03/12
259
0
php中Session使用方法详解

php中Session使用方法详解 Session的声明与使用 Session的设置不同于Cookie,必须先启动,在PHP中必须调用sessionstart()。sessionstart()函数的语法格式如下: Bool session_start(void) /...

优惠券活动
2018/05/08
0
0
PHP单点登录

之前回答过同样的问题,我重新拿出来再回答一遍。题主请看 首先提醒一下题主不要被新技术吓到了,也不要去查什么SSO(Single Sign On,单点登录),那样只能增大你问题的复杂度和对这个问题的...

蜗牛奔跑
2016/11/20
68
0
九、参考资料

《实战 Nginx:取代Apache的高性能Web服务器》 《nginx的五种负载均衡算法》 《Nginx服务器优化》 《nginx 解决session共享》 《nginx tomcat session复制》 《UNIX下Apache 简介及基本配置》...

孟飞阳
2016/08/07
50
0

没有更多内容

加载失败,请刷新页面

加载更多

为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
25分钟前
2
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
36分钟前
2
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
36分钟前
2
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
42分钟前
6
0
IDEA 自定义方法注解模板

IDEA 自定义方法注解模板 1、使用效果 /*** 计算交易费用* @Author wangjiafang* @Date 2019/9/11* @param feeComputeVo* @return*/@PostMapping("/v1/fee_compute")public ApiResp......

小白的成长
42分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部