从输入URL到页面加载完成的过程-TCP三次握手

从输入URL到页面加载完成的过程-TCP三次握手原文地址

在上篇文章中,通过DNS获取服务器端ip后,客户端组装数据准备发送请求,但是由于没有和服务端建立可靠的连接,所以需要通过TCP的三次握手建立连接:所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息,在socket编程中,客户端执行connect()时,将触发三次握手,即客户端是主动打开连接,服务端是被动打开连接。

1、第一次握手:

  • 客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。发送后客户端进入SYN_SEND(同步已发送)状态,等待服务器确认。注意报文不能携带数据。

    2、第二次握手:

  • 服务器收到请求报文段后,如同意建立连接,则向客户端发送确认。服务器发回确认包(ACK)应答,即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的ISN加1以,即ack=X+1,同时也为自己选择一个初始序号seq=y。注意报文也不能携带数据,TCP服务器进程进入SYN-RCVD(同步收到)状态。

    3、第三次握手:

  • TCP客户端进程收到服务端的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+1。发送报文段后,TCP连接已经简历,A进入ESTABLISHED(已建立连接)状态。

图:TCP三次握手简历连接

注意:为什么会有第三次握手,假设一种情况,客户端第一次发送请求连接报文,滞留在网络中,此时客户端又发送了一次,这次和服务端建立连接,操作完后并释放了连接。此时第一次发送的请求报文到达服务端,那么服务端发送同意连接,并等待客户端发来数据,此时客户端收到同意连接报文,因为操作已经执行过,所以丢弃这个报文。服务端会一直等待,那么就会浪费服务端的资源。

  • 补充一个SYN攻击:

    • 在三次握手过程中,服务器发送SYN-ACK之后,收到客户端的ACK之前的TCP连接称为半连接(half-open connect).此时服务器处于Syn_RECV状态.当收到ACK后,服务器转入ESTABLISHED状态.

    • Syn攻击就是 攻击客户端 在短时间内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直 至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。

  • Syn攻击是一个典型的DDOS攻击。检测SYN攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击.在Linux下可以如下命令检测是否被Syn攻击

netstat -ntp | grep SYN_RECV
一般较新的TCP/IP协议栈都对这一过程进行修正来防范Syn攻击,修改tcp协议实现。主要方法有SynAttackProtect保护机制、SYN cookies技术、增加最大半连接和缩短超时时间等.

但是不能完全防范syn攻击。

到此大家应该了解到建立连接是多么的麻烦,需要经过三次发包,如果是网络延迟大的时候,请求的延迟将更加严重,那有一些什么办法进行优化呢?

把服务器放到离用户近的地方以减少往返时间;
尽最大可能重用已经建立的TCP连接。
还有一种就是简化建立连接的过程,就是TCP Fast Open草案,期待它早点实施吧

建立连接后,客户端就要向服务端发送请求页面的报文段了,那么这个请求是怎么封装成帧进行透明传输的呢?请看下篇组装数据包的过程。

从输入URL到页面加载完成的过程-DNS解析域名过程

从输入URL到页面加载完成的过程-DNS解析域名过程

原文链接

当我们输入一个url,比如为:http://my.oschina.net/u/128568/blog,浏览器会解析这个url分成三部分,域名:my.oschina.net,端口:80,请求资源:/u/128568/blog。下面给出浏览器查找域名对应IP的过程,域名为my.oschina.net:

1、浏览器会缓存DNS记录一段时间。 有趣的是,操作系统没有告诉浏览器储存DNS记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)

2、操作系统检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。

3、如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析

4、如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/IP参数中设置的首选DNS服务器(ISP的DNS),在此我们叫它本地DNS服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性

5、如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性

6、如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至 “根DNS服务器”,“根DNS服务器”收到请求后会判断这个域名(.net)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.net域的这台服务器。这台负责.net域的服务器收到请求后,如果自己无法解析,它就会找一个管理.net域的下一级DNS服务器地址(oschina.net)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找oschina.net域服务器,重复上面的动作,进行查询,直至找到my.oschina.net主机

7、如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。不管是本地DNS服务器用是是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机

看下解析过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
➜ /Users/zyj >dig +trace my.oschina.net

; <<>> DiG 9.8.3-P1 <<>> +trace my.oschina.net
;; global options: +cmd
. 191557 IN NS e.root-servers.net.
. 191557 IN NS i.root-servers.net.
. 191557 IN NS a.root-servers.net.
. 191557 IN NS k.root-servers.net.
. 191557 IN NS g.root-servers.net.
. 191557 IN NS j.root-servers.net.
. 191557 IN NS h.root-servers.net.
. 191557 IN NS c.root-servers.net.
. 191557 IN NS l.root-servers.net.
. 191557 IN NS f.root-servers.net.
. 191557 IN NS d.root-servers.net.
. 191557 IN NS b.root-servers.net.
. 191557 IN NS m.root-servers.net.
;; Received 228 bytes from 114.114.114.114#53(114.114.114.114) in 39 ms

net. 172800 IN NS m.gtld-servers.net.
net. 172800 IN NS l.gtld-servers.net.
net. 172800 IN NS k.gtld-servers.net.
net. 172800 IN NS j.gtld-servers.net.
net. 172800 IN NS i.gtld-servers.net.
net. 172800 IN NS h.gtld-servers.net.
net. 172800 IN NS g.gtld-servers.net.
net. 172800 IN NS f.gtld-servers.net.
net. 172800 IN NS e.gtld-servers.net.
net. 172800 IN NS d.gtld-servers.net.
net. 172800 IN NS c.gtld-servers.net.
net. 172800 IN NS b.gtld-servers.net.
net. 172800 IN NS a.gtld-servers.net.
;; Received 489 bytes from 198.41.0.4#53(198.41.0.4) in 136 ms

oschina.net. 172800 IN NS ns1.dnsv2.com.
oschina.net. 172800 IN NS ns2.dnsv2.com.
;; Received 205 bytes from 192.52.178.30#53(192.52.178.30) in 558 ms

my.oschina.net. 86400 IN CNAME www.oschina.net.
oschina.net. 7200 IN NS ns1.dnsv2.com.
oschina.net. 7200 IN NS ns2.dnsv2.com.
;; Received 115 bytes from 183.60.59.230#53(183.60.59.230) in 15 ms

DNS有一点令人担忧,这就是像www.taobao.com这样的整个域名看上去只是对应一个单独的IP地址,那么三个IP肯定对于大访问量和不同地域的访问来说就会有较大的延迟。还好,有几种方法可以消除这个瓶颈:

  • 循环DNS 是DNS查找时返回多个IP时的解决方案。举例来说,taobao.com实际上就对应了多个IP地址。

  • 负载平衡器 是以一个特定IP地址进行侦听并将网络请求转发到集群服务器上的硬件设备。 一些大型的站点一般都会使用这种昂贵的高性能负载平衡器。

  • 地理 DNS 根据用户所处的地理位置,通过把域名映射到多个不同的IP地址提高可扩展性。这样不同的服务器不能够更新同步状态,但映射静态内容的话非常好。

  • Anycast 是一个IP地址映射多个物理主机的路由技术。 美中不足,Anycast与TCP协议适应的不是很好,所以很少应用在那些方案中。

大多数DNS服务器使用Anycast来获得高效低延迟的DNS查找。

最后给出一个查询DNS过程的图:

最终获取的域名对应IP为112.124.5.74,此时浏览器发起向112.124.5.74的请求,端口为80,请求资源为/u/128568/blog,发送一条HTTP GET的报文,下面看下浏览器请求的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Remote Address:112.124.5.74:80
Request URL:http://my.oschina.net/u/128568/blog
Request Method:GET
Status Code:200 OK

#请求头信息
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
Connection:keep-alive
Cookie:
oscid=Zka95GnBtcz%2FTWwBZdIK1f%2BrZzKNNUN0vu5av2CocDrEpbLcUPBTht%2FIFGWopqXUjIysP943EaQtn4%2FJdlcX%2FOW65EUP%2Fgo7fzPdxBwqLALbq7SVBQoTFw%3D%3D
Host:my.oschina.net
User-Agent:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36

#响应头信息
Response Headers
Cache-Control:must-revalidate, no-cache, private
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html;charset=UTF-8
Date:Sun, 25 May 2014 05:23:43 GMT
Expires:Sun, 1 Jan 2000 01:00:00 GMT
Pragma:no-cache
Server:Tengine/1.4.6
Transfer-Encoding:chunked
Vary:Accept-Encoding

从请求和响应可以看到很多信息,比如使用GET,响应为200,传输使用了压缩gzip等等,需要说明的是最后这个Connection:keep-alive,是为了兼容HTTP1.0设置的,用来告诉服务器使用持久连接。

以Apache服务器、php语言为例详解动态网站的访问过程

以Apache服务器、php语言为例详解动态网站的访问过程

原文链接

  • 网站页面主要分为静态页面和动态页面,纯静态页面组成的网站现在相对比较少见,大型网站一般使用的是动态网站建站技术,还有一部分网站是静态网页与动态网页共存,本文以Apache服务器、php语言为例,详解动态网站的访问过程,下面直接切入本文主题。

(1)用户端访问服务器端的html文件

S1:通过本机配置好的DNS域名服务器地址寻找DNS服务器,将网站URL中的Web主机域名解析为Web服务器所在的Linux操作系统(Apache通常与Linux操作系统组合使用)中对应的IP地址。

S2:通过HTTP协议(超文本传输协议)去连接上述IP地址的服务器系统,通过默认80端口(默认的端口是80,也有其他端口,输入URL时一般不用输入端口)请求Apache服务器上相应目录下的html文件(如index.htm)。

S3:Apache服务器收到用户的访问请求后,在它管理的文档目录中找到并打开相应的html文件(如index.htm),将文件内容响应给客户端浏览器(即用户)。

S4:浏览器收到Web服务器的响应后,接收并下载服务器端的html静态代码,然后浏览器解读代码,最终将网页呈现出来(由于不同的浏览器对于代码的解读规则会不一样,所以不同浏览器对于相同的网页呈现的最终页面效果会有所差异)。

(2)用户端访问服务器端的php文件

S1:该步与上面访问html静态网页是一样的,通过DNS服务器解析出相应的Web服务器的IP地址。

S2:与上面访问html静态页面相似,不过最后请求的是Apache服务器上相应目录下的php文件,如index.php。

S3:Apache服务器本身不能处理php动态语言脚本文件,就寻找并委托PHP应用服务器来处理(服务器端事先得安装PHP应用服务器),Apache服务器将用户请求访问的php文件(如index.php)文件交给PHP应用服务器。

S4:PHP应用服务器接收php文件(如index.php),打开并解释php文件,最终翻译成html静态代码,再将html静态代码交还给Apache服务器,Apache服务器将接收到的html静态代码输出到客户端浏览器(即用户)。

S5:与上面访问html静态页面相同,浏览器收到Web服务器的响应后,接收并下载服务器端的html静态代码,然后浏览器解读代码,最终将网页呈现出来。

(3)用户端访问服务器端的MySQL数据库

如果用户需要对MySQL数据库中的数据进行操作,那么就需要在服务器端安装数据库管理软件MySQL服务器,用来存储和管理网站数据。由于Apache服务器是无法连接和操作MySQL服务器,所以还需要安装php应用服务器,这样Apache服务器就委托php应用服务器去连接和操作数据库,在对数据库中的数据进行管理的时候,一般都需要用到结构化查询语句,即SQL语句。

S1:该步与上面访问php文件一样,通过DNS服务器解析出相应的Web服务器的IP地址。

S2:与上面访问php文件一样,请求访问Apache服务器上相应目录下的php文件。

S3:与上面访问php文件一样,PHP应用服务器接收Apache服务器的委托,收到相应的php文件。

S4:PHP应用服务器打开php文件,在php文件中通过对数据库连接的代码来连接本机或者网络上其他机器上的MySQL数据库,并在php程序中通过执行标准的SQL查询语句来获取数据库中的数据,再通过PHP应用服务器将数据生成html静态代码。

S5:浏览器收到Web服务器的响应后,接收并下载服务器端的html静态代码,然后浏览器解读代码,最终将网页呈现出来。

需要注意的是,文中(2)与(3)的区别是,一个访问了数据库,另一个没有访问数据库,所以在过程中有点差别。

本文首发于叶德华的博客:http://www.cheaplululemon.com,转载请注明网站地址。

PHP$_POST 变量

PHP$_POST 变量

  • 预定义的 $_POST 变量用于收集来自 method=”post” 的表单中的值。

从带有 POST 方法的表单发送的信息,对任何人都是不可见的(不会显示在浏览器的地址栏),并且对发送信息的量也没有限制。

然而,默认情况下,POST 方法的发送信息的量最大值为 8 MB(可通过设置 php.ini 文件中的 post_max_size 进行更改)。

1
2
3
4
5
<form action="welcome.php" method="post">
Name: <input type="text" name="fname">
Age: <input type="text" name="age">
<input type="submit">
</form>

当用户点击 “Submit” 按钮时,URL 如下所示:

http://www.w3cschool.cc/welcome.php

“welcome.php” 文件现在可以通过 $_POST 变量来收集表单数据了(请注意,表单域的名称会自动成为 $_POST 数组中的键):

Welcome <?php echo $_POST[“fname”]; ?>!

You are <?php echo $_POST[“age”]; ?> years old.

何时使用 method=”post”?

从带有 POST 方法的表单发送的信息,对任何人都是不可见的,并且对发送信息的量也没有限制。

然而,由于变量不显示在 URL 中,所以无法把页面加入书签。

PHP $_REQUEST 变量

预定义的 $_REQUEST 变量包含了 $_GET、$_POST 和 $_COOKIE 的内容。
$_REQUEST 变量可用来收集通过 GET 和 POST 方法发送的表单数据。

1
2
Welcome <?php echo $_REQUEST["fname"]; ?>!<br>
You are <?php echo $_REQUEST["age"]; ?> years old.

之前至此的几篇关于php基础的知识均来自http://www.runoob.com/php/php-tutorial.html之中,文章为转载

PHP$_GET 变量

PHP $_GET 变量

  • 在 PHP 中,预定义的 $_GET 变量用于收集来自 method=”get” 的表单中的值。

  • 从带有 GET 方法的表单发送的信息,对任何人都是可见的(会显示在浏览器的地址栏),并且对发送信息的量也有限制。

1
2
3
4
5
<form action="welcome.php" method="get">
Name: <input type="text" name="fname">
Age: <input type="text" name="age">
<input type="submit">
</form>

当用户点击 “Submit” 按钮时,发送到服务器的 URL 如下所示:

http://www.w3cschool.cc/welcome.php?fname=Peter&age=37

“welcome.php” 文件现在可以通过 $_GET 变量来收集表单数据了(请注意,表单域的名称会自动成为 $_GET 数组中的键):

Welcome <?php echo $_GET[“fname”]; ?>.

You are <?php echo $_GET[“age”]; ?> years old!

何时使用 method=”get”?

在 HTML 表单中使用 method=”get” 时,所有的变量名和值都会显示在 URL 中。

所以在发送密码或其他敏感信息时,不应该使用这个方法!

然而,正因为变量显示在 URL 中,因此可以在收藏夹中收藏该页面。在某些情况下,这是很有用的。

HTTP GET 方法不适合大型的变量值。它的值是不能超过 2000 个字符的。

php中json_decode()和json_encode()的使用方法*转

1.json_decode()

json_decode
(PHP 5 >= 5.2.0, PECL json >= 1.2.0)

json_decode — 对 JSON 格式的字符串进行编码

说明
mixed json_decode ( string $json [, bool $assoc ] )
接受一个 JSON 格式的字符串并且把它转换为 PHP 变量

参数

json
待解码的 json string 格式的字符串。

assoc
当该参数为 TRUE 时,将返回 array 而非 object 。

返回值
Returns an object or if the optional assoc parameter is TRUE, an associative array is instead returned.

范例

Example #1 json_decode() 的例子

1
2
3
4
5
6
7
代码如下:

<?php
$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
var_dump(json_decode($json));
var_dump(json_decode($json, true));
?>

上例将输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

object(stdClass)#1 (5) {
["a"] => int(1)
["b"] => int(2)
["c"] => int(3)
["d"] => int(4)
["e"] => int(5)
}

array(5) {
["a"] => int(1)
["b"] => int(2)
["c"] => int(3)
["d"] => int(4)
["e"] => int(5)
}
1
2
3
4
 代码如下:

$data='[{"Name":"a1","Number":"123","Contno":"000","QQNo":""},{"Name":"a1","Number":"123","Contno":"000","QQNo":""},{"Name":"a1","Number":"123","Contno":"000","QQNo":""}]';
echo json_decode($data);

结果为:

1
2
3
 代码如下:

Array ( [0] => stdClass Object ( [Name] => a1 [Number] => 123 [Contno] => 000 [QQNo] => ) [1] => stdClass Object ( [Name] => a1 [Number] => 123 [Contno] => 000 [QQNo] => ) [2] => stdClass Object ( [Name] => a1 [Number] => 123 [Contno] => 000 [QQNo] => ) )

可以看出经过json_decode()编译出来的是对象,现在输出json_decode($data,true)试下

1
2
3
 代码如下:

echo json_decode($data,true);

结果:

1
2
3
 代码如下:

Array ( [0] => Array ( [Name] => a1 [Number] => 123 [Contno] => 000 [QQNo] => ) [1] => Array ( [Name] => a1 [Number] => 123 [Contno] => 000 [QQNo] => ) [2] => Array ( [Name] => a1 [Number] => 123 [Contno] => 000 [QQNo] => ) )

可以看出 json_decode($data,true)输出的一个关联数组,由此可知json_decode($data)输出的是对象,而json_decode(“$arr”,true)是把它强制生成PHP关联数组.

2.json_encode()

json_encode
(PHP 5 >= 5.2.0, PECL json >= 1.2.0)

json_encode — 对变量进行 JSON 编码

Report a bug 说明
string json_encode ( mixed $value [, int $options = 0 ] )
返回 value 值的 JSON 形式

Report a bug 参数

value
待编码的 value ,除了resource 类型之外,可以为任何数据类型

该函数只能接受 UTF-8 编码的数据

options
由以下常量组成的二进制掩码: JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT, JSON_UNESCAPED_UNICODE.

Report a bug 返回值
编码成功则返回一个以 JSON 形式表示的 string 或者在失败时返回 FALSE 。

Report a bug 更新日志
版本 说明
5.4.0 options 参数增加常量: JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, 和 JSON_UNESCAPED_UNICODE。
5.3.3 options 参数增加常量:JSON_NUMERIC_CHECK。
5.3.0 增加 options 参数.

Report a bug 范例

Example #1 A json_encode() 的例子

1
2
3
4
5
6
7
 代码如下:

<?php
$arr = array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);

echo json_encode($arr);
?>

以上例程会输出:

1
2
3
 代码如下:

{"a":1,"b":2,"c":3,"d":4,"e":5}

Example #2 json_encode() 函数中 options 参数的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 代码如下:

<?php
$a = array('<foo>',"'bar'",'"baz"','&blong&', "\xc3\xa9");

echo "Normal: ", json_encode($a), "\n";
echo "Tags: ", json_encode($a, JSON_HEX_TAG), "\n";
echo "Apos: ", json_encode($a, JSON_HEX_APOS), "\n";
echo "Quot: ", json_encode($a, JSON_HEX_QUOT), "\n";
echo "Amp: ", json_encode($a, JSON_HEX_AMP), "\n";
echo "Unicode: ", json_encode($a, JSON_UNESCAPED_UNICODE), "\n";
echo "All: ", json_encode($a, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE), "\n\n";

$b = array();

echo "Empty array output as array: ", json_encode($b), "\n";
echo "Empty array output as object: ", json_encode($b, JSON_FORCE_OBJECT), "\n\n";

$c = array(array(1,2,3));

echo "Non-associative array output as array: ", json_encode($c), "\n";
echo "Non-associative array output as object: ", json_encode($c, JSON_FORCE_OBJECT), "\n\n";

$d = array('foo' => 'bar', 'baz' => 'long');

echo "Associative array always output as object: ", json_encode($d), "\n";
echo "Associative array always output as object: ", json_encode($d, JSON_FORCE_OBJECT), "\n\n";
?>

以上例程会输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 代码如下:

Normal: ["<foo>","'bar'","\"baz\"","&blong&","\u00e9"]
Tags: ["\u003Cfoo\u003E","'bar'","\"baz\"","&blong&","\u00e9"]
Apos: ["<foo>","\u0027bar\u0027","\"baz\"","&blong&","\u00e9"]
Quot: ["<foo>","'bar'","\u0022baz\u0022","&blong&","\u00e9"]
Amp: ["<foo>","'bar'","\"baz\"","\u0026blong\u0026","\u00e9"]
Unicode: ["<foo>","'bar'","\"baz\"","&blong&","é"]
All: ["\u003Cfoo\u003E","\u0027bar\u0027","\u0022baz\u0022","\u0026blong\u0026","é"]

Empty array output as array: []
Empty array output as object: {}

Non-associative array output as array: [[1,2,3]]
Non-associative array output as object: {"0":{"0":1,"1":2,"2":3}}

Associative array always output as object: {"foo":"bar","baz":"long"}
Associative array always output as object: {"foo":"bar","baz":"long"}

Example #3 连续与非连续数组示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 代码如下:

<?php
echo "连续数组".PHP_EOL;
$sequential = array("foo", "bar", "baz", "blong");
var_dump(
$sequential,
json_encode($sequential)
);

echo PHP_EOL."非连续数组".PHP_EOL;
$nonsequential = array(1=>"foo", 2=>"bar", 3=>"baz", 4=>"blong");
var_dump(
$nonsequential,
json_encode($nonsequential)
);

echo PHP_EOL."删除一个连续数组值的方式产生的非连续数组".PHP_EOL;
unset($sequential[1]);
var_dump(
$sequential,
json_encode($sequential)
);
?>

以上例程会输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 代码如下:

连续数组
array(4) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
[2]=>
string(3) "baz"
[3]=>
string(5) "blong"
}
string(27) "["foo","bar","baz","blong"]"

非连续数组
array(4) {
[1]=>
string(3) "foo"
[2]=>
string(3) "bar"
[3]=>
string(3) "baz"
[4]=>
string(5) "blong"
}
string(43) "{"1":"foo","2":"bar","3":"baz","4":"blong"}"

删除一个连续数组值的方式产生的非连续数组
array(3) {
[0]=>
string(3) “foo”
[2]=>
string(3) “baz”
[3]=>
string(5) “blong”
}
string(33) “{“0”:”foo”,”2”:”baz”,”3”:”blong”}”

1
2
3
4
5
 代码如下:

$obj->Name= 'a1';$obj->Number ='123';
$obj->Contno= '000';
echo json_encode($obj);

结果为:

1
2
3
4
5
6
 代码如下:

{"Name":"a1",
"Number":"123",
"Contno":"000"
}

可以看出json_encode()和json_decode()是编译和反编译过程,注意json只接受utf-8编码的字符,所以json_encode()的参数必须是utf-8编码,否则会得到空字符或者null。

PHP完整表单实例

PHP 完整表单实例

本章节将介绍如何让用户在点击”提交(submit)”按钮提交数据前保证所有字段正确输入。

  • PHP - 在表单中确保输入值

在用户点击提交按钮后,为确保字段值是否输入正确,我们在HTML的input元素中插添加PHP脚本, 各字段名为: name, email, 和 website。 在评论的 textarea 字段中,我们将脚本放于 < textarea> 和 < /textarea> 标签之间。 PHP脚本输出值为: $name, $email, $website, 和 $comment 变量。

然后,我们同样需要检查被选中的单选按钮, 对于这一点,我们 必须设置好checked属性(不是radio按钮的 value 属性) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Name: <input type="text" name="name" value="<?php echo $name;?>">

E-mail: <input type="text" name="email" value="<?php echo $email;?>">

Website: <input type="text" name="website" value="<?php echo $website;?>">

Comment: <textarea name="comment" rows="5" cols="40"><?php echo $comment;?></textarea>

Gender:
<input type="radio" name="gender"
<?php if (isset($gender) && $gender=="female") echo "checked";?>
value="female">Female
<input type="radio" name="gender"
<?php if (isset($gender) && $gender=="male") echo "checked";?>
value="male">Male

PHP - 完整表单实例

以下是完整的PHP表单验证实例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<!DOCTYPE HTML> 
<html>
<head>
<style>
.error {color: #FF0000;}
</style>
</head>
<body>

<?php
// define variables and set to empty values
$nameErr = $emailErr = $genderErr = $websiteErr = "";
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST")
{
if (empty($_POST["name"]))
{$nameErr = "Name is required";}
else
{
$name = test_input($_POST["name"]);
// check if name only contains letters and whitespace
if (!preg_match("/^[a-zA-Z ]*$/",$name))
{
$nameErr = "Only letters and white space allowed";
}
}

if (empty($_POST["email"]))
{$emailErr = "Email is required";}
else
{
$email = test_input($_POST["email"]);
// check if e-mail address syntax is valid
if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email))
{
$emailErr = "Invalid email format";
}
}

if (empty($_POST["website"]))
{$website = "";}
else
{
$website = test_input($_POST["website"]);
// check if URL address syntax is valid (this regular expression also allows dashes in the URL)
if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i",$website))
{
$websiteErr = "Invalid URL";
}
}

if (empty($_POST["comment"]))
{$comment = "";}
else
{$comment = test_input($_POST["comment"]);}

if (empty($_POST["gender"]))
{$genderErr = "Gender is required";}
else
{$gender = test_input($_POST["gender"]);}
}

function test_input($data)
{
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
?>

<h2>PHP Form Validation Example</h2>
<p><span class="error">* required field.</span></p>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
Name: <input type="text" name="name" value="<?php echo $name;?>">
<span class="error">* <?php echo $nameErr;?></span>
<br><br>
E-mail: <input type="text" name="email" value="<?php echo $email;?>">
<span class="error">* <?php echo $emailErr;?></span>
<br><br>
Website: <input type="text" name="website" value="<?php echo $website;?>">
<span class="error"><?php echo $websiteErr;?></span>
<br><br>
Comment: <textarea name="comment" rows="5" cols="40"><?php echo $comment;?></textarea>
<br><br>
Gender:
<input type="radio" name="gender" <?php if (isset($gender) && $gender=="female") echo "checked";?> value="female">Female
<input type="radio" name="gender" <?php if (isset($gender) && $gender=="male") echo "checked";?> value="male">Male
<span class="error">* <?php echo $genderErr;?></span>
<br><br>
<input type="submit" name="submit" value="Submit">
</form>

<?php
echo "<h2>Your Input:</h2>";
echo $name;
echo "<br>";
echo $email;
echo "<br>";
echo $website;
echo "<br>";
echo $comment;
echo "<br>";
echo $gender;
?>

</body>
</html>

PHP表单验证邮件和URL

PHP 表单验证邮件和URL

PHP - 验证名称

以下代码将通过简单的方式来检测 name 字段是否包含字母和空格,如果 name 字段值不合法,将输出错误信息:

1
2
3
4
5
$name = test_input($_POST["name"]);
if (!preg_match("/^[a-zA-Z ]*$/",$name))
{
$nameErr = "只允许字母及空格";
}

preg_match — 进行正则表达式匹配。

语法:
int preg_match ( string $pattern , string $subject [, array $matches [, int $flags ]] )

在 subject 字符串中搜索与 pattern 给出的正则表达式相匹配的内容。如果提供了 matches ,则其会被搜索的结果所填充。$matches[0] 将包含与整个模式匹配的文本,$matches[1] 将包含与第一个捕获的括号中的子模式所匹配的文本,以此类推。

PHP - 验证邮件

以下代码将通过简单的方式来检测 e-mail 地址是否合法。如果 e-mail 地址不合法,将输出错误信息:

1
2
3
4
5
$email = test_input($_POST["email"]);
if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email))
{
$emailErr = "非法邮件地址";
}

PHP - 验证 URL

以下代码将检测URL地址是否合法 (以下正则表达式运行URL中含有破折号:”-“), 如果 URL 地址不合法,将输出错误信息:

1
2
3
4
5
$website = test_input($_POST["website"]);
if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i",$website))
{
$websiteErr = "不合法的 URL";
}

PHP - 验证 Name, E-mail, 和 URL

代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
// 定义变量并设为空值
$nameErr = $emailErr = $genderErr = $websiteErr = "";
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST")
{
if (empty($_POST["name"]))
{$nameErr = "Name is required";}
else
{
$name = test_input($_POST["name"]);
// check if name only contains letters and whitespace
if (!preg_match("/^[a-zA-Z ]*$/",$name))
{
$nameErr = "Only letters and white space allowed";
}
}

if (empty($_POST["email"]))
{$emailErr = "Email is required";}
else
{
$email = test_input($_POST["email"]);
// check if e-mail address syntax is valid
if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email))
{
$emailErr = "Invalid email format";
}
}

if (empty($_POST["website"]))
{$website = "";}
else
{
$website = test_input($_POST["website"]);
// check if URL address syntax is valid (this regular expression also allows dashes in the URL)
if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i",$website))
{
$websiteErr = "Invalid URL";
}
}

if (empty($_POST["comment"]))
{$comment = "";}
else
{$comment = test_input($_POST["comment"]);}

if (empty($_POST["gender"]))
{$genderErr = "Gender is required";}
else
{$gender = test_input($_POST["gender"]);}
}
?>

PHP表单验证

PHP 表单验证

  • 在处理PHP表单时我们需要考虑安全性,为了防止黑客及垃圾信息我们需要对表单进行数据安全验证。
  • 下面的HTML表单中包含以下输入字段: 必须与可选文本字段,单选按钮,及提交按钮:

上述表单验证规则如下:

文本字段

name, email, 及 website 字段为文本输入元素, comment 字段是 textarea。HTML代码如下所示:

1
2
3
4
Name: <input type="text" name="name">
E-mail: <input type="text" name="email">
Website: <input type="text" name="website">
Comment: <textarea name="comment" rows="5" cols="40"></textarea>

单选按钮

gender 字段是单选按钮,HTML代码如下所示:

1
2
3
Gender:
<input type="radio" name="gender" value="female">Female
<input type="radio" name="gender" value="male">Male

表单元素

HTML 表单代码如下所示:

1
2
3
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
该表单使用 method="post" 方法来提交数据。
Note 什么是 $_SERVER["PHP_SELF"] 变量?

  • $_SERVER[“PHP_SELF”]是超级全局变量,返回当前正在执行脚本的文件名,与 document root相关。
    所以, $_SERVER[“PHP_SELF”] 会发送表单数据到当前页面,而不是跳转到不同的页面。

什么是 htmlspecialchars()方法?

1
2
3
4
5
6
7
htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体。
预定义的字符是:
& (和号) 成为 &amp;
" (双引号) 成为 &quot;
' (单引号) 成为 &#039;
< (小于) 成为 &lt;
> (大于) 成为 &gt;

PHP表单中需引起注重的地方?

$_SERVER[“PHP_SELF”] 变量有可能会被黑客使用!

当黑客使用跨网站脚本的HTTP链接来攻击时,$_SERVER[“PHP_SELF”]服务器变量也会被植入脚本。原因就是跨网站脚本是附在执行文件的路径后面的,因此$_SERVER[“PHP_SELF”]的字符串就会包含HTTP链接后面的JavaScript程序代码。

XSS又叫 CSS (Cross-Site Script) ,跨站脚本攻击。恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。

1
2
3
4
5
6
7
8
9
10
指定以下表单文件名为 "test_form.php":
<form method="post" action="<?php echo $_SERVER["PHP_SELF"];?>">
现在,我们使用URL来指定提交地址 "test_form.php",以上代码修改为如下所示:
<form method="post" action="test_form.php">
这样做就很好了。
但是,考虑到用户会在浏览器地址栏中输入以下地址:
http://www.runoob.com/test_form.php/%22%3E%3Cscript%3Ealert('hacked')%3C/script%3E
以上的 URL 中,将被解析为如下代码并执行:
<form method="post" action="test_form.php/"><script>alert('hacked')</script>
代码中添加了 script 标签,并添加了alert命令。 当页面载入时会执行该Javascript代码(用户会看到弹出框)。 这仅仅只是一个简单的实例来说明PHP_SELF变量会被黑客利用。

请注意,任何JavaScript代码可以添加在< script>标签中! 黑客可以利用这点重定向页面到另外一台服务器的页面上,页面 代码文件中可以保护恶意代码,代码可以修改全局变量或者获取用户的表单数据,

  • 如何避免 $_SERVER[“PHP_SELF”] 被利用?
    1
    2
    3
    4
    5
    6
    $_SERVER["PHP_SELF"] 可以通过 htmlspecialchars() 函数来避免被利用。
    form 代码如下所示:
    <form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
    htmlspecialchars() 把一些预定义的字符转换为 HTML 实体。现在如果用户想利用 PHP_SELF 变量, 结果将输出如下所示:
    <form method="post" action="test_form.php/&quot;&gt;&lt;script&gt;alert('hacked')&lt;/script&gt;">
    尝试该漏洞失败!

使用 PHP 验证表单数据

  • 首先我们对用户所有提交的数据都通过 PHP 的 htmlspecialchars() 函数处理。

当我们使用 htmlspecialchars() 函数时,在用户尝试提交以下文本域:
< script>location.href(‘http://www.runoob.com‘)< /script>
该代码将不会被执行,因为它会被保存为HTML转义代码,如下所示:

1
&lt;script&gt;location.href('http://www.runoob.com')&lt;/script&gt;

以上代码是安全的,可以正常在页面显示或者插入邮件中。

  • 当用户提交表单时,我们将做以下两件事情,:

1.使用 PHP trim() 函数去除用户输入数据中不必要的字符 (如:空格,tab,换行)。

2.使用PHP stripslashes()函数去除用户输入数据中的反斜杠 ()

接下来让我们将这些过滤的函数写在一个我们自己定义的函数中,这样可以大大提高代码的复用性。
将函数命名为 test_input()。
现在,我们可以通过test_input()函数来检测 $_POST 中的所有变量, 脚本代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// 定义变量并默认设置为空值
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST")
{
$name = test_input($_POST["name"]);
$email = test_input($_POST["email"]);
$website = test_input($_POST["website"]);
$comment = test_input($_POST["comment"]);
$gender = test_input($_POST["gender"]);
}

function test_input($data)
{
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
?>
  • 注意我们在执行以上脚本时,会通过$_SERVER[“REQUEST_METHOD”]来检测表单是否被提交 。如果 REQUEST_METHOD 是 POST, 表单将被提交 数据将被验证。如果表单未提交将跳过验证并显示空白。

在以上实例中使用输入项都是可选的,即使用户不输入任何数据也可以正常显示。

PHP表单之必需字段

PHP 表单 - 必需字段

我们将介绍如何设置表单必需字段及错误信息。

PHP - 必需字段
在上一章节我们已经介绍了表的验证规则,我们可以看到”Name”, “E-mail”, 和 “Gender” 字段是必须的,各字段不能为空

在以下代码中我们加入了一些新的变量: $nameErr, $emailErr, $genderErr, 和 $websiteErr.。这些错误变量将显示在必须字段上。 我们还为每个$_POST变量增加了一个if else语句。 这些语句将检查 $_POST 变量是 否为空(使用php的 empty() 函数)。如果为空,将显示对应的错误信息。 如果不为空,数据将传递给test_input() 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
// 定义变量并默认设为空值
$nameErr = $emailErr = $genderErr = $websiteErr = "";
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST")
{

if (empty($_POST["name"]))
{$nameErr = "Name is required";}
else
{$name = test_input($_POST["name"]);}

if (empty($_POST["email"]))
{$emailErr = "Email is required";}
else
{$email = test_input($_POST["email"]);}

if (empty($_POST["website"]))
{$website = "";}
else
{$website = test_input($_POST["website"]);}

if (empty($_POST["comment"]))
{$comment = "";}
else
{$comment = test_input($_POST["comment"]);}

if (empty($_POST["gender"]))
{$genderErr = "Gender is required";}
else
{$gender = test_input($_POST["gender"]);}
}
?>

PHP - 显示错误信息

在以下的HTML实例表单中,我们为每个字段中添加了一些脚本, 各个脚本会在信息输入错误时显示错误信息。(如果用户未填写信息就提交表单则会输出错误信息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

Name: <input type="text" name="name">
<span class="error">* <?php echo $nameErr;?></span>
<br><br>
E-mail:
<input type="text" name="email">
<span class="error">* <?php echo $emailErr;?></span>
<br><br>
Website:
<input type="text" name="website">
<span class="error"><?php echo $websiteErr;?></span>
<br><br>
<label>Comment: <textarea name="comment" rows="5" cols="40"></textarea>
<br><br>
Gender:
<input type="radio" name="gender" value="female">Female
<input type="radio" name="gender" value="male">Male
<span class="error">* <?php echo $genderErr;?></span>
<br><br>
<input type="submit" name="submit" value="Submit">

</form>

,