一、会话概述
1.1、技术背景
互联网通信中采用的Http协议(建立TCP连接->Http请求->Http应答->断开TCP连接)本身是无状态的,即Http各请求之间是相互独立、互不相关的,而大量应用需要将各请求关联起来(如:用户登录系统购物、多次购买行为需要与该用户绑定),因此需要有一种机制实现Http各请求之间的关联
1.2、会话定义
用户端与服务端进行网络通信,用户从登录进入系统到注销退出系统,这一完整过程称为会话(Session);会话中,用户端与服务端进行的一序列通信处理(Http请求与应答)将始终与登录的具体用户相关联
1.3、实现原理
服务端收到用户端Http请求后,先从Http请求包中获取会话ID(如果没有,将自动生成一个会话ID),根据会话ID在内存中生成对应的散列表(如:PHP中的$_SESSION[])、检索上次保存的会话记录(如果检索不到,将自动生成一条与该会话ID绑定的空会话记录),将会话记录中的数据加载到散列表,通过散列表读写当前会话数据,最后请求处理结束时将散列表中的会话数据保存为会话记录、脚本结束时将当前的会话ID放入Http应答包发送给用户端(供下次Http请求使用)
二、会话ID的传递
从上述会话实现原理可知,会话ID将一序列Http数据包(请求/应答)关联起来,从而构成一个完整的会话,整个会话过程中需要在用户端与服务端通过Http数据包传递会话ID,具体传递方式有:Cookie方式传递、URL参数方式传递、数据内容方式传递,具体如下
2.1、Cookie方式传递
2.1.1、传递原理
Http请求过程:
用户端(如:浏览器)先从本地(内存或磁盘)读取保存的对应Cookie数据(名称、值、域名等),再将读取到的相应数据填写到Http请求数据包头的Cookie字段(首次请求时,由于本地无Cookie数据,该字段为空),而后将Http请求数据包发送到服务端;服务端收到Http请求数据包后解析时,将根据当前的会话名称匹配对应的Cookie名称,如果匹配到了、则读取对应的值(会话ID)作为当前的会话ID,如果匹配不到、将自动生成一个当前的会话ID;该过程中,用户端从本地读取保存的Cookie原则为:Cookie所声明的作用范围(由其路径与域共同决定)大于等于将要请求的资源所在的位置
Http应答过程:
服务端先将当前的会话名称、会话ID等数据分别填入Http应答数据包头的Cookie字段,而后将Http应答数据包发送到用户端;用户端收到Http应答数据包进行解析,再将解析得到的Cookie数据(名称、值、域名等)进行保存(至于保存到内存还是磁盘,由具体的程序设计决定,一般原则为:如果Cookie设置了有效时间,就保存到磁盘,否则将保存到内存)
2.1.2、环境配置
用户端配置:
使用Cookie方式传递时,用户端必须开启Cookie的接受与发送功能,否则将不能实现该传递功能;不同用户端开启的方法不一样,以下为IE浏览器开启Cookie接受与发送功能的相关步骤:
菜单栏“工具”->“Internet选项”->“隐私”->“选择Internet区域设置”->选择“接受所有Cookie”->点“确定”关闭
服务端配置:
使用Cookie方式传递时,服务端必须开启Cookie的接受与发送功能(一般默认为开启状态),否则将不能实现该传递功能;开启方法如下:
方法1:配置文件“php.ini”中的“session.use_cookies”配置项设置为“1”->重新启动服务器(如:Apache)
方法2:用ini_set()函数直接在PHP脚本中将“session.use_cookies”配置项设置为“1”
2.1.3、案例演示
创建如下PHP脚本文件(ByCookie.php):
/*
* ByCookie.php
*/
//打开错误及告警提示(不打开E_NOTICE,否则下面使用$_SESSION会有告警)
error_reporting((E_ALL | E_STRICT) & (~E_NOTICE));
//error_reporting(E_ALL | E_STRICT);
//设置会话名称
session_name('TransID');
//启动新会话或者重用现有会话
$startRst = session_start();
if (!$startRst)
{
echo 'Session start fail!'.'
';
return ;
}
//获取当前会话名称
$sessName = session_name();
echo 'session name:'.$sessName.'
';
//获取当前会话ID
$sessId = session_id();
echo 'session id:'.$sessId.'
';
//读写当前会话数据
echo 'Last request,session data:'.$_SESSION['count'].'
';
if (empty($_SESSION['count']))
{
$_SESSION['count'] = 1;
}
else
{
$_SESSION['count']++;
}
echo 'This request,session data:'.$_SESSION['count'].'
';
?>
在浏览器中用多个页面顺序打开上述“ByCookie.php”文件多次,便可以看到被多次传递的同一个会话ID及相关数据,结果如下:
2.2、URL参数方式传递
2.2.1、传递原理
首次请求时,服务端将当前的会话名称及会话ID放入Http应答数据包响应正文(作为参数附加到URL后)中发送到用户端;用户端通过URL触发连接请求(如:点击链接或提交表单)时,会话名称及会话ID被填写到Http请求数据包头的URL字段(作为参数附加到URL后),而后将Http请求数据包发送到服务端;服务端收到Http请求数据包后,通过$_GET[]全局变量即可得到请求数据包头URL字段中的参数(会话名称及会话ID);如果还有需要,服务端再将会话名称及会话ID放入Http应答数据包响应正文(作为参数附加到URL后)中发送到用户端、供用户端再次通过URL触发连接请求
2.2.2、案例演示
在同一目录下创建如下2个PHP脚本文件(ByURLSend.php及ByURLRcve.php):
文件1(ByURLSend.php):
/*
* ByURLSend.php
*/
//打开错误及告警提示(不打开E_NOTICE,否则下面使用$_SESSION会有告警)
error_reporting((E_ALL | E_STRICT) & (~E_NOTICE));
//error_reporting(E_ALL | E_STRICT);
//设置会话名称
session_name('TransID');
//启动新会话或者重用现有会话
$startRst = session_start();
if (!$startRst)
{
echo 'Session start fail!'.'
';
return ;
}
//获取当前会话名称
$sessName = session_name();
echo 'session name:'.$sessName.'
';
//获取当前会话ID
$sessId = session_id();
echo 'session id:'.$sessId.'
';
//读写当前会话数据
echo 'Last request,session data:'.$_SESSION['count'].'
';
if (empty($_SESSION['count']))
{
$_SESSION['count'] = 1;
}
else
{
$_SESSION['count']++;
}
echo 'This request,session data:'.$_SESSION['count'].'
';
?>
==session_id();?>">URL参数方式传递(方法1)
">URL参数方式传递(方法3)
文件2(ByURLRcve.php):
/*
* ByURLRcve.php
*/
//打开错误及告警提示(不打开E_NOTICE,否则下面使用$_SESSION会有告警)
error_reporting((E_ALL | E_STRICT) & (~E_NOTICE));
//error_reporting(E_ALL | E_STRICT);
//设置会话名称
session_name('TransID');
//获取当前会话名称
$sessName = session_name();
//获取当前会话ID
$sessId = $_GET[$sessName];
if ('' == $sessId)
{
echo 'Session id transmits fail!'.'
';
return ;
}
//设置当前会话ID
session_id($sessId);
//启动新会话或者重用现有会话
$startRst = session_start();
if (!$startRst)
{
echo 'Session start fail!'.'
';
return ;
}
//获取当前会话名称
$sessName = session_name();
echo 'session name:'.$sessName.'
';
//获取当前会话ID
$sessId = session_id();
echo 'session id:'.$sessId.'
';
//读写当前会话数据
echo 'Last request,session data:'.$_SESSION['count'].'
';
if (empty($_SESSION['count']))
{
$_SESSION['count'] = 1;
}
else
{
$_SESSION['count']++;
}
echo 'This request,session data:'.$_SESSION['count'].'
';
?>
在浏览器中打开“ByURLSend.php”,便能看到如下结果:
点击不同的链接,便可以看到在不同页面间传递的同一个会话ID及相关数据
2.3、数据内容方式传递
2.3.1、传递原理
首次请求时,服务端将当前的会话名称及会话ID放入Http应答数据包响应正文(通常以字段值的方式放置)中发送到用户端;用户端通过POST方法触发Http请求时,会话名称及会话ID作为Http请求数据包的请求数据发送到服务端;服务端收到Http请求数据包后,直接解析Http请求数据包的请求数据即可得到会话名称及会话ID;如果还有需要,服务端再将会话名称及会话ID放入Http应答数据包响应正文(通常以字段值的方式放置)中发送到用户端、供用户端再次POST方法触发Http请求
2.3.2、案例演示
在同一目录下创建如下2个PHP脚本文件(ByDataSend.php及ByDataRcve.php):
文件1(ByDataSend.php):
/*
* ByDataSend.php
*/
//打开错误及告警提示(不打开E_NOTICE,否则下面使用$_SESSION会有告警)
error_reporting((E_ALL | E_STRICT) & (~E_NOTICE));
//error_reporting(E_ALL | E_STRICT);
//设置会话名称
session_name('TransID');
//重用现有会话;
$startRst = session_start();
if (!$startRst)
{
echo 'Session start fail!'.'
';
return ;
}
//获取当前会话名称
$sessName = session_name();
echo 'session name:'.$sessName.'
';
//获取当前会话ID
$sessId = session_id();
echo 'session id:'.$sessId.'
';
//读写当前会话数据
echo 'Last request,session data:'.$_SESSION['count'].'
';
if (empty($_SESSION['count']))
{
$_SESSION['count'] = 1;
}
else
{
$_SESSION['count']++;
}
echo 'This request,session data:'.$_SESSION['count'].'
';
?>
文件2(ByDataRcve.php):
/*
* ByDataRcve.php
*/
//打开错误及告警提示(不打开E_NOTICE,否则下面使用$_SESSION会有告警)
error_reporting((E_ALL | E_STRICT) & (~E_NOTICE));
//error_reporting(E_ALL | E_STRICT);
//设置会话名称
session_name('TransID');
//获取当前会话名称
$sessName = session_name();
//获取当前会话ID
$sessId = $_POST[$sessName];
if ('' == $sessId)
{
echo 'Session id transmits fail!'.'
';
return ;
}
//设置当前会话ID
session_id($sessId);
//重用现有会话;
$startRst = session_start();
if (!$startRst)
{
echo 'Session start fail!'.'
';
return ;
}
//获取当前会话名称
$sessName = session_name();
echo 'session name:'.$sessName.'
';
//获取当前会话ID
$sessId = session_id();
echo 'session id:'.$sessId.'
';
//读写当前会话数据
echo 'Last request,session data:'.$_SESSION['count'].'
';
if (empty($_SESSION['count']))
{
$_SESSION['count'] = 1;
}
else
{
$_SESSION['count']++;
}
echo 'This request,session data:'.$_SESSION['count'].'
';
?>
在浏览器中打开“ByDataSend.php”,便能看到如下结果:
点击“数据内容方式传递”按钮,便可以看到在不同页面间传递的同一个会话ID