ここ数日間、私はヘッダー、Cookie、セッション、キャッシュ、および http プロトコルのその他の関連知識を検討し、いくつかの新しい知識点を発見しました。
この記事では、これらの内容を理解するために PHP を組み合わせることに焦点を当てています。つまり、実践的な部分により重点を置いています。
NO1: Web アプリケーションの場合、ユーザー グループはクライアント (さまざまなブラウザー) 上の任意の接続をクリックしてサーバーに http リクエストを送信します。このプロセスでは接続を確立するために 3 回のハンドシェイクが必ず必要となり、サーバーはデータを返すことで応答します。 。
すべてのリクエストにはヘッダーとエンティティ部分があります。まず、作成者が QQ スペースで監視するヘッダーを見てみましょう。QQ スペースの理由は、そのヘッダーの内容が比較的完全であるためです。
Request Headers: GET http://user.qzone.qq.com/445235728 HTTP/1.1 Host: user.qzone.qq.com Connection: keep-alive Cache-Control: max-age=0 User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Referer: http://qzone.qq.com/ Accept-Encoding:gzip,deflate,sdch Accept-Language: zh-CN,zh;q=0.8 Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3 Cookie:o_cookie=445235728;(省略很多……) If-Modified-Since: Wed, 13 Jun 2012 01:32:19 GMT ----------------- Response Headers: HTTP/1.1 200 OK Connection:close Server: QZHTTP-2.34.0 Date: Wed, 13 Jun 2012 02:59:31 GMT Content-Encoding: gzip Set-Cookie:login_time=61F0EEA02D704B1DBCF25166A74941B24F4BE24B205C466F;PATH=/; DOMAIN=qzone.qq.com Set-Cookie:Loading=Yes;expires=Wed,13-Jun-201216:00:00GMT;PATH=/;DOMAIN=qzone.qq.com X-UA-Compatible: IE=Edge Last-Modified: Wed, 13 Jun 2012 02:59:31 GMT Cache-Control: max-age=0, no-transform Content-Type: text/html;charset=utf-8 Transfer-Encoding: chunked
1. クライアント要求ヘッダーには、要求行といくつかのヘッダー フィールドが含まれます。
リクエスト行: リクエスト メソッドの URL (Uniform Resource Identifier) プロトコル バージョン -------3 つをスペースで区切って、最後に新しい行 (rn) を入力します。例: GET http://user.qzone。 qq .com/445235728 HTTP/1.1
さまざまなヘッダー フィールド: これらのヘッダー フィールドは、キーワードとキー値のペアで構成され、最後に改行とキャリッジ リターン (rn) で終わります。これらのヘッダー フィールドは、サーバーに応答方法と独自のフィールドの一部を指示します。情報。
2. サーバーの応答
ステータス行: プロトコルのバージョン、応答ステータス、ステータスの説明 --これら 3 つはスペースで区切られ、最後に改行とキャリッジ リターン (rn) が続きます。例: HTTP/1.1 200 OK
。さまざまなヘッダー フィールド: これらのヘッダー フィールドには、キーワードとキー値のペアの組み合わせもあり、最後に改行とキャリッジ リターン (rn) で終わります。これらのヘッダー フィールドは、クライアントに応答方法と独自の情報の一部を伝えます。
NO2
ここでは各ヘッダー フィールドの概念と機能については説明しません。詳しく知りたい場合は、http://www.phpben.com/?post=34 を参照してください。重要だと思われるいくつかのテストは、一部の Web サイトで使用されています。データとリクエストの戻りヘッダー フィールドの php コード実装
。テスト時期: 2012.6.14 以前
テスト対象: csdn、cnbeta、cnblos、Tencent (QQ Zone、Friends.com、News Network)、Sina (Weibo、ホームページ)、Renren、Baidu、Taobao、Youku、Tudou およびその他の Web サイト
(1) 接続ヘッダー フィールド: このヘッダー フィールドは http/1.1 でのみ使用できます。デフォルト値は、長い接続を示すキープアライブ値です。この方法では、接続のたびにサーバーに接続する必要はありません。画像、CSS ファイル、JS ファイルなどのリソースが要求される場合は、3 回のハンドシェイク接続を実行します。これにより、HTTP ステートレス性の欠点がある程度補われ、サーバーへの接続時間が短縮されます。
テスト Web サイトの Connection ヘッダー フィールドを見ると、Tencent QQ Zone、Tencent News Network、Sina Home Page、Weibo、Youku、Tudou の接続: これらを除き、その他は接続: keep-alive であることがわかりました。
なぜ?
1. connection: keep-alive を通常に使用するための前提条件は、本文の長さをクライアントに伝えるために content-length ヘッダー フィールドを提供することです。テキストの長さの計算は問題ですが、マルチコンテンツ サーバーやクラスター サーバーではこれが容易ではありません。 Tencent、Sina、Youku ではこれらを計算するのは難しく、エンジニアには非公開になっています (デフォルトは公開されています)。
2. 古いサーバーはサポートしていません。Tencent や Sina などの古いサーバーでは、サーバー クラスターが非常に大きいため、互換性の問題により、一部の古いサーバーは直接閉じられます。
追伸: これら 2 つの理由は検証されていません。 ^-^
php ヘッダーを使用する(“Connection:keep-alive”);
(2) Content-Encoding ヘッダーフィールド
Content-Encoding ドキュメントのエンコード (Encode) メソッド。
上記の Web サイトは cnbeta に gzip 圧縮を使用していませんが、Youku は deflate を使用しており、残りは同じです。これは、PHPer が圧縮 gzip 送信テクノロジを習得する必要があるという重要なメッセージも明らかにしています。
Php は mod_gzip モジュールを通じて実装できます。コード: ob_start("ob_gzhandler");
(3) Server ヘッダー フィールドは、サーバーの重要なセキュリティ情報を公開します。
CSDN: サーバー:nginx/0.7.68 ------------ バージョンが公開されています
Tencent QQ Space: サーバー: QZHTTP-2.34.0--------TX 友人によると、これは社内で開発されたサーバーであり、十分安全であることが明らかになりました
新浪微博: サーバー:apache -------------これはリークされたバージョンではありません
Ifeng.com: サーバー: nginx/0.8.53
レンレン: サーバー:nginx/1.2.0
淘宝: Tengine --------これは、淘宝網の内部技術チームによって開発された、Nginx に基づいた Web サーバーです
cnblogs ブログ パーク: サーバー:Microsoft-IIS/7.5
Tencent Friends Network: Tencent/PWS --------Tencent 内部開発
Tencent News Network: サーバー:squid/3.1.18
Youku.com: サーバー: fswww1-----------内部的なものかどうかはわかりませんが、少なくともどこから来たのかはわかりません^_^
Tudou: Tengine/1.2.3
Baidu: サーバー: BWS/1.0 ----------Baidu が社内で開発したサーバーである必要があります
サーバーヘッダーフィールドはサーバーに返される情報であることは明らかですが、この問題に直面して、大企業は独自の機能に基づいて独自の社内サーバーを開発します。
(4) X-Powered-By ヘッダー フィールドは変更可能であり、セキュリティに基づいて変更できます
X-Powered-By ヘッダー フィールドは、バックグラウンド プログラムが実行される言語とバージョンを反映します。これは同じヘッダー関数で変更できます
header("X-Powered-By:acb");
(5) Cache-control、expires、last-modified等重要头域
Cache-control:指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
Php代码实现:header("cache-control: abc");abc是上述指令值一个或多个,多个用’,’分开
Expires:告诉浏览器指明应该在什么时候认为文档已经过期,从而不再缓存它。代码实现:header("Expires:". date('D, d M Y H:i:s \G\M\T', time()+10));--------这个是把时间截转化成格林时区字符串给expires头域,这个显示时间会比中国北京时间少8个小时,东8区的实现:header("Expires:". date('r', time()+10))
last-modified:这个是服务器返回给浏览器,浏览器下次请求则把该值赋给if-modified-since头域传给服务器,服务器就可以根据此值判断是否有改变,有则继续运行下去,否者返回304 not modified。Php设置expires头域一样。
代码:
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < 10)) { header("HTTP/1.1 304 Not Modified"); exit; } header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );或者header("Last-Modified: " . date('r', time()) );
前者是格林时间格式,后者是中国时间。需要注意的就是 php.ini 的时区 prc 则用后则,否者前者。笔者曾经试过在时区是 prc 的情况下用了前者,导致 time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) <0 永远成立,因为是负值。
注意:当请求页面有session_start()的时候,则不管是否有expires、cache-control、last-modified设置,则返回给客户端Cache-Control头域为Cache-Control:no-store, no-cache, must-revalidate Expires头域 Expires:Thu, 19 Nov 1981 08:52:00 GMT。这个问题烦了笔者2天,都以为php.ini 或是apache的问题。最后竟然是session_start()的问题。
前面介绍了http headers几个告诉浏览器如何处理缓存。但不同浏览器处理各种头域的方式不同,以下就是笔者。
Ps:各个浏览器监听http headers的方法可以查看:http://www.phpben.com/?post=76
(1) header(“cache-control: no-store”)
IE9
Google17.0
Firefox11
Maxthon3
点击刷新键
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
地址栏回车
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
点击后退键
同上
同上
同上
同上
(2) header(“cache-control: no-cache”)
IE9
Google17.0
Firefox11
Maxthon3
点击刷新键
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
地址栏回车
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
点击后退键
同上
From cache
From cache
同上
(3) header(“cache-control:bublic”)
IE9
Google17.0
Firefox11
Maxthon3
点击刷新键
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
地址栏回车
from cache
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
点击后退键
From cache
From cache
From cache
同上
(4) header("cache-control:private"); header("cache-control: must-revalidate ")
IE9
Google17.0
Firefox11
Maxthon3
点击刷新键
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
地址栏回车
除第一次外都是from cache
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
点击后退键
From cache
From cache
From cache
同上
(5) header("cache-control:max-age=num");num是秒数
IE9
Google17.0
Firefox11
Maxthon3
点击刷新键
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
重发请求,返回200状态
地址栏回车
秒数 重发请求,返回200状态 重发请求,返回200状态 重发请求,返回200状态 点击后退键 From cache From cache From cache 同上 (6) IE9 Google17.0 Firefox11 Maxthon3 点击刷新键 重发请求,返回200状态 重发请求,返回200状态 重发请求,返回200状态 重发请求,返回200状态 地址栏回车 秒数 重发请求,返回200状态 重发请求,返回200状态 重发请求,返回200状态 点击后退键 From cache From cache From cache 同上 (7) IE9 Google17.0 Firefox11 Maxthon3 点击刷新键 秒数 秒数 秒数 重发请求,返回200状态 地址栏回车 from cache 秒数 秒数 重发请求,返回200状态 点击后退键 From cache From cache From cache 同上 结论: 1、 刷新对于任何浏览器且不管是什么cache-control,都会重新请求,一般返回是200,除非Last-Modified设置 2、 后退键除非no-cache; no-store外都是使用缓存 3、 Cache-control:no-store 在浏览器中任何操作都重新提交请求,包括后退 4、 遨游3的缓存很差 5、 IE9 的缓存很强,所以用ie9调试的时候尽可能点刷新而不是在地址栏回车 鉴于这种情况,对于不同的应用(有些要缓存,有些经常更新)对于不同的国家各种浏览器份额,而哪种缓存方式。中国IE比较多,加上360浏览器的加入(用IE内核),那就要主要参照IE浏览器。 但笔者还是比较喜欢header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );这种方式。结合起来connection:keep-alive能让缓存技术更成熟。 注意: 1、 也许你会问,用Cache-control:no-store或Cache-control:no-store,但调试页面还是没原来的缓存。然后清除浏览器缓存关掉重启浏览器, 缓存还在。这是因为你的web应用用了文件缓存如ecshop常出现这种情况,这种情况就要进web应用后台删除文件缓存。 2、 调试的时候尽可能不要在地址栏回车,特别是IE,google还好一点,但是要知道这次的测试只是各个浏览器中的一个版本,所以调试的时候尽可能点刷新按钮。 3、 但在cache-control:max-age=num 和expires 一起使用的时候,前者级别比较高,浏览器会忽略expires的设置。(上面没给出测试内容) Session 、cookies是程序员永远讨论的话题之一。 1、 简单说一下cookies、session (1) Cookies是保存在客户端的小段文本,随客户端点每一个请求发送该url下的所有cookies到服务器端。比如在谷歌浏览器下,打开ww.abc.com下的两个文件,a.php包含cookies1和cookies2,b.php包含了cookies3和cookies4,那么在a.php或b.php 点任意一个连接(当然是ww.abc.com服务器上的),浏览器就会把cookies1~4这4个cookies发送给服务器。但是如果在IE9有打开一个包含cookies5的c.php,哪门在google浏览器点击连接是不会发送cookies5的。 (2) Session则保存服务器段,通过唯一的值sessionID来区别每一个用户。SessionID随每个连接请求发送到服务器,服务器根据sessionID来识别客户端,再通过session 的key获取session值。SessionID传回服务器的实现方式可以通过cookies和url回写来实现。 注意: 1、 同一个浏览器打开同一个文件,如a.php ,或同时有设置session的两个文件a.php、b.php sessionID则只有一个。(时间上不能是打开a.php 关闭浏览器再打开b.php) 2、 不同浏览器在同一时间打开同意文件的sessionID也不一样 3、 sessionID是服务器生成的不规则唯一字符串,如: PHPSESSID=05dbfffd3453b7be02898fdca4fcd82b;------ PHPSESSID可以通过php.ini中session.name来改变,所以笔者在监听一些大型网站的时候查不出PHPSESSID,这是一个安全因素。 (3) cookies、session在php中的主要相关参数 (1) session.save_handler = ”files” 默认以文件方式存取session数据,如果想要使用自定义的处理器来存取session数据,比如数据库,用”user”。 (2) session.use_cookies = 1 前面说到sessionID用cookies来实现,这里就是,1表示用cookies (3) session.use_trans_sid = 0 上面是用cookies来实现sessionID,这里值若是1则使用url回写方式,级别比session.use_cookies高 (4) session.use_only_cookies = 0 值为1则sessionID只可以用cookies实现,级别比前两个高 (5) session.cache_expire =180 session 缓存过期的秒数 (6) session.gc_maxlifetime = 1440 设定保存的session文件生存期,超过此参数设定秒数后,保存的数据将被视为’垃圾’并由垃圾回收程序清理。判断标准是最后访问数据的时间(对于FAT文件系统是最后刷新数据的时间)。如果多个脚本共享同一个session.save_path目录但session.gc_maxlifetime不同,将以所有session.gc_maxlifetime指令中的最小值为准。 (4) 图说cookie 、ssession php代码如下 结论: 1、 第一次请求是没用cookies的,而第二次有PHPSESSID和两个cookies是因为服务器第一请求返回这个三个cookies。 2、第二次请求比第一次多返回PHPSESSID这个cookies,在第二次则没有了,直到session过期后重新设置。 2、 ;浏览器关掉cookies,session是否可以正常运行? 前面提及sessionID的时候有两种方式。 (1) cookies 方式,在session.use_trans_sid=0 and session.use_cookies = 1的情况下使用。这种方法是每次浏览器端点每个请求,都把sessionID发送到服务器。 (2) url回写,session.use_only_cookies = 0 and session.use_trans_sid=1的情况下,服务器会忽略session.use_trans_sid,在浏览器发hhtp请求后,服务器会在返回页面内容中每个连接后面加上PHPSESSID=05dbfffd3453b7be02898fdca4fcd82b (在php.ini没改session.name,默认是PHPSESSID),这样就算客户端的浏览器禁止了cookies,一样能实现session功能。 这里来个测试: 在1.php文件代码: 在 2.php 文件代码: 情景1:没禁用浏览器的cookies(用cookies实现session),则在2.php能正常输出 情景2:禁用用浏览器的cookies且在php.ini开启session.use_trans_sid=1,通过1.php第一连接过去显示不了session的值,但第二个连接则正常显示。(说明url回写正常运行)
header("Expires:". date('D, d M Y H:i:s \G\M\T', time()+num)); num是秒数
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (time()-strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < num)) {
header("HTTP/1.1 304 Not Modified");
exit;
} header("Last-Modified: " . date('D, d M Y H:i:s \G\M\T', time()) );
三、 Session和cookies
session_start();
$_SESSION['favcolor'] = 'green';
$_SESSION['animal'] = 'cat';
$_SESSION['time'] = time();
setcookie("cookie1","www.scutephp.com",time()+3600*10);
setcookie("cookie2","www.scutephp.com",time()+3600*10);
echo 'Welcome to page #1<br/>';
session_start();
$_SESSION['favcolor'] = 'green';
$_SESSION['animal'] = 'cat';
$_SESSION['time'] = time();
// Works if session cookie was accepted
echo '<br /><a href="2.php">page 1 (这个href中没SID参数)</a><br/>';
// Or maybe pass along the session id, if needed
echo '<br /><a href="2.php?' . SID . '">page 2 (这个href中有SID参数)</a><br/>';
session_start();
echo 'Welcome to page #2<br />';
echo $_SESSION['favcolor'],'<br/>'; // green
echo $_SESSION['animal'],'<br/>'; // cat
echo date('Y m d H:i:s', $_SESSION['time']),'<br/>';
// You may want to use SID here, like we did in page1.php
echo '<br /><a href="1.php">return page 1</a>';