Session 知识点再整理(一)基本概念和原理

Session 的概念

Session 和 Cookie 一样,也是针对 HTTP 的局限性而提出的一种保持客户端和服务器端会话连接状态的机制。

Session 被称为会话,指用户在进入网站到浏览器关闭(或退出网站)这段时间内与 Web 系统的会话过程。

Session 的存储 

Session 保存在服务器端,默认情况以文件的形式保存在服务器硬盘上,每个 Session 一个文件,文件名如:sess_j64kv3np0ft2u00aun0cilqdo2,里面保存的内容结构是:变量名 | 类型:长度:值;

例如:

name|s:3:"dee";level|i:2;

在 php.ini 中,session.save_handler 定义了存储和获取 Session 数据的处理器的名字,默认为 files,表示使用文件形式保存 Session:

session.save_handler = files

也可以设置成用户自定义方式存储 Session 的 user 或者 memcache 来使 Session 保存到关系型数据库或者 Memcache 等内存服务器中。

在 php.ini 中,session.save_path 表示 Session 存储的位置,在 WAMPSERVER 环境下的默认值是:

session.save_path = "d:/wamp/tmp"

表示 Session 文件存储在 d 盘 wamp/tmp 目录下; Windows 环境中在该配置被注释的情况下 Session 可能保存在 C:WindowsTemp 目录下,Linux 环境中在该配置被注释的情况下 Session 保存在 /tmp 或 /var/lib/php/session 目录下。

当 Session 保存在 Memcache 中时可以修改该配置,例如:

session.save_path = "tcp://127.0.0.1:11211,tcp://127.0.0.1:11212"

以上配置代表将 Session 保存到两台 Memcache 服务器中。

当 Session 以文件形式保存时,默认是保存在硬盘的一个目录下,当 Session 比较多时,磁盘读取文件会比较慢,通常一个目录下文件数量超过 2000 个时,读写这个目录就会比较慢。此时可以考虑将 Session 分目录存放。session.save_path 有一个可选的 N 参数来决定 Session 文件分布的目录深度,格式是:

session.save_path = "N;MODE;/path"

N 表示要设置的目录级数,MODE 表示目录的权限属性,默认为 600(Windows 中不需要设置),/path 表示 Session 文件存放的根目录路径每一级目录分别以 0 - 9 和 a - z 共 36 个字符作为目录名,这样存放 Session 的目录可以达到 36^36 个。创建文件夹的工作需要 PHP 来完成。

例如,2 级目录即 N = 2 时可以这样设置:

<?php
//设置Session目录分级与保存路径
ini_set('session.save_path', "2;/wamp/tmp");

//创建2级目录
$string = '0123456789abcdefghijklmnopqrstuvwxyz';
$length = strlen($string);
for($i = 0; $i < $length; $i++) {
    for($j = 0; $j < $length; $j++) {
        createfolder('d:/wamp/tmp/'.$string[$i].'/'.$string[$j]);
    }
}

function createfolder($path) {
    if(!@file_exists($path)) {
        createfolder(@dirname($path));
        @mkdir($path, 0777);
    }
}

session_start();
$_SESSION['name'] = 'dee';

echo session_id();

打印出的 Session ID 为 3b9ptga94p7b5v1s33dnacrf95,那么 PHP 会根据该 ID 的前两位字符来存储 Session 文件,即该 Session 文件存储在 d:/wamp/tmp/3/b 下:

如果使用了 N 参数并且大于 0,那么 PHP 将不会自动回收过期的 Session,需要通过 shell 或者 cron 来清除过期的文件。

Session 的开启

通过 session_start 函数创建新会话或者重用现有会话,在 session_start 函数之前页面不能有任何的输出内容(当 php.ini 中 的output_buffering = Off 时)。

当自动开启会话或者通过 session_start() 手动开启会话的时候, PHP 内部会调用会话管理器(save_handler)的 open 、 read 和 gc 回调函数。

会话管理器默认为文件(file), 也可以是 SQLite 或者  Memcached 扩展, 还可以通过 session_set_save_handler() 设定的用户自定义会话管理器,并通过 read 回调函数返回现有的(经过序列化的)会话数据, PHP 会自动反序列化数据并且赋给 $_SESSION 超级全局变量。

Session ID 及其传递

Session 的唯一标识 Session ID,是由 32 位十六进制数组成的字符串,php.ini 中 session.use_cookies 的默认值为 1,即默认通过 Cookie 来进行保存和传递:

session.use_cookies = 1

通过设置也能在客户端禁用了 Cookie 之后通过 URL 或者隐藏表单进行 session_id 的 GET 或 POST 传递。

如果需要在 Cookie 被禁用时仍然能使用 Session,需要修改 php.ini 中的几个配置:session.use_only_cookies 和 session.use_trans_sid,这两个参数的默认值分别是 1 和 0,分别代表在客户端仅仅使用 Cookie 来存放 Session ID ,和指定不启用透明 Session ID 支持(即在 URL 中不自动加上 Session ID 的参数):

session.use_only_cookies = 1
session.use_trans_sid = 0

需要把这两个配置修改为:

session.use_only_cookies = 0
session.use_trans_sid = 1

此时如果禁用了 Cookie,则 URL 的格式为 格式为 a.php?PHPSESSID=session_id

将 session_id 通过 URL 进行传递的缺点有:1. 安全性,将 session_id 暴露在了 URL 中 2. 当禁用了 Cookie 后,如果 URL 中没有传递 session_id(例如重复访问第一个设置 Session 的页面),就会使系统不能重用现有会话而重新生成新的会话。

例 在 FireFox 40.0.3 下,设置禁用 Cookie:

”工具“ -- ”选项“ -- ”隐私“ -- ”使用自定义历史记录设置“ -- 取消勾选 ”接收来自站点的Cookie“

session_url_set.php:

<?php
header("Content-type:text/html;charset=utf-8");

ini_set('session.use_trans_sid', 1);
ini_set('session.use_only_cookies', 0);

session_start();

$_SESSION['user'] = 'dee';

echo session_id();
echo '<pre>';
print_r($_SESSION);
echo '<a href="session_url_get.php" target="_blank">跳转</a>';

输出:

session_url_get.php

<?php

ini_set('session.use_trans_sid', 1);
ini_set('session.use_only_cookies', 0);

session_start();

echo session_id();
echo '<pre>';
print_r($_SESSION);

输出:

session_id 自动成为 URL 参数。

但是此时再次刷新 session_url_set.php 时,会重新生成新的 Session:

当用户向服务器服务器发出请求时,服务器会首先检查请求中是否已经包含了 Session ID(通过请求头中 Cookie 中的 PHPSESSID 或者 URL 中的参数 PHPSESSID ),如果包含则服务器会重复使用该 Session ID,如果不包含,则生成一个新的 Session ID。

注意:URL 自动带上 Session ID 参数的条件是 ① 当前 Cookie 中不包含 Session ID ;② session.use_trans_sid 设置为 1 ;③ session.use_only_cookies 设置为 0

可以通过修改 php.ini 中的 session.name 来修改 Seeesion Name,默认这是 PHPSESSID:

session.name = PHPSESSID

可以通过在 PHP 脚本中输入 session_name() 来查看当点的 Session Name :

echo session_name();

可以通过 session_id() 来查看当前的 Session ID:

echo session_id();

输出的值和对应的 Session 文件名相对应,例如 Session ID 是 j64kv3np0ft2u00aun0cilqdo2,文件名是 sess_j64kv3np0ft2u00aun0cilqdo2。

注销变量和销毁 Session

Session 的注销包括 4 个步骤:

<?php

//1.开启 Session
session_start();

//2.删除所有的 Session 变量
$_SESSION = array();

//3.如果是基于 Cookie 的 Session,则删除该 Cookie
if(isset($_COOKIE[session_name()])) {
    setcookie(session_name(), '', time() - 1, '/');
}

//4.彻底销毁 Session
session_destroy();

此时 保存 Session ID 的 Cookie已经被清除,Session 文件的内容已经被清空,但是服务器上的 Session 文件的删除则需要依赖 Session 的垃圾回收机制。

Session 的生存周期和垃圾回收机制

默认情况下,Session ID 存储在 Cookie 中,该 Cookie 默认设置过期时间为 0,即随着浏览器关闭而消失(Cookie 保存在内存中),php.ini 的设置:

session.cookie_lifetime = 0

关闭浏览器后该 Session ID 自动注销,如果重新请求该页面则会重新注册一个新的 Session ID。

但是 Session 文件可能仍然存在,原因是 Session 的垃圾回收机制。和 Session 的垃圾回收机制相关的函数是:session_start(),在调用该函数时启动回收机制,相关的 3 个配置分别是 :session.gc_maxlifetime,表示 Session 文件的过期时间,默认为 1440 秒即 24分钟能、session.gc_probabilitysession.gc_divisor,后面两个配置的默认值分别是 1 和 1000,代表 session_start() 函数每调用 1000 次触发一次 Session 文件的全部扫描,把过期的 Session 文件删除:

session.gc_maxlifetime = 1440
session.gc_probability = 1
session.gc_divisor = 1000

过期的 Session 文件的判断标准是:文件的修改时间和当前时间相差是否大于 session.gc_maxlifetime。当用户每进行一个操作哪怕是一个刷新页面的动作时,都会修改 Session 文件的修改时间。

可以手动设置 Session 的生命周期:

$life_time = 3600 * 24;
setcookie(session_name(), session_id(), time() + $life_time, '/');

经过设置,在关闭浏览器后,保存 Session ID 的 Cookie 的生命周期达到了 24 小时,也就是在 24 小时内即使关闭了浏览器,下次再访问该页面时 PHP 会重用该 Session。 

可以通过设置较短的 session.gc_maxlifetime 和 较小的 session.gc_divisor 来观察 Session 的过期机制:

把 session.gc_maxlifetime 设为 10(秒),即 10 秒内用户没有任何操作(包括刷新)的话,10 秒后该会话即成为过期 Session;把 session.gc_divisor 分别设为 1 和 2,也就是 session.gc_probability /  session.gc_divisor = 1 或 1/2,意思是每一 或 两次访问网站(页面),都将扫描所有的 Session 文件,同时将过期的 Session 文件从硬盘上删除(当 Session 以 files 形式存储时),来观察 Session 的垃圾回收机制。注意 session.cookie_lifetime 要和 session.gc_maxlifetime 设置为相同的值。

session_cookie_set.php

<?php
header("Content-type:text/html;charset=utf-8");

ini_set('session.cookie_lifetime', 10);
ini_set('session.gc_maxlifetime', 10);
ini_set('session.gc_divisor', 1);

session_start();

$_SESSION['username'] = 'deathmask';

echo session_id();
echo '<pre>';
print_r($_SESSION);
echo '<a href = "session_cookie_get.php" target="_blank">跳转</a>';

 session_cookie_get.php

<?php

ini_set('session.cookie_lifetime', 10);
ini_set('session.gc_maxlifetime', 10);
ini_set('session.gc_divisor', 1);

session_start();

echo session_id();
echo '<pre>';
print_r($_SESSION);

观察硬盘上 Session 文件的删除情况。

参考:

session.save_path目录大量session临时文件带来的服务器效率问题 

PHP设置session定期自动清理的例子

PHP session 暫存檔過多的注意事項

PHP Session文件的散列存储及过期删除

原文地址:https://www.cnblogs.com/dee0912/p/5049692.html