HTTP协议详解

HTTP协议详解
Author: Kevin | Posted: 19. 八月 2009 21:15 0 引言                                        
HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出。 
HTTP协议的主要特点可概括如下: 
1.支持客户/服务器模式。 
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。 
3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 
4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 
5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。 
一、HTTP协议详解之URL篇 
    http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。 
HTTP URL (URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息)的格式如下: 
http://host[":"port][abs_path] 
http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用缺省端口80;abs_path指定请求资源的URI;如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动帮我们完成。 
eg: 
1、输入:www.guet.edu.cn 
浏览器自动转换成:http://www.guet.edu.cn/ 
2、http:192.168.0.116:8080/index.jsp 
二、HTTP协议详解之请求篇 
    http请求由三部分组成,分别是:请求行、消息报头、请求正文 
1、请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method Request-URI HTTP-Version CRLF  
其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。 
请求方法(所有方法全为大写)有多种,各个方法的解释如下: 
GET     请求获取Request-URI所标识的资源 
POST    在Request-URI所标识的资源后附加新的数据 
HEAD    请求获取由Request-URI所标识的资源的响应消息报头 
PUT     请求服务器存储一个资源,并用Request-URI作为其标识 
DELETE  请求服务器删除Request-URI所标识的资源 
TRACE   请求服务器回送收到的请求信息,主要用于测试或诊断 
CONNECT 保留将来使用 
OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求 
应用举例: 
GET方法:在浏览器的地址栏中输入网址的方式访问网页时,浏览器采用GET方法向服务器获取资源,eg:GET /form.html HTTP/1.1 (CRLF) 
POST方法要求被请求服务器接受附在请求后面的数据,常用于提交表单。 
eg:POST /reg.jsp HTTP/ (CRLF) 
Accept:image/gif,image/x-xbit,... (CRLF) 
... 
HOST:www.guet.edu.cn (CRLF) 
Content-Length:22 (CRLF) 
Connection:Keep-Alive (CRLF) 
Cache-Control:no-cache (CRLF) 
(CRLF)         //该CRLF表示消息报头已经结束,在此之前为消息报头 
user=jeffrey&pwd=1234  //此行以下为提交的数据 
HEAD方法与GET方法几乎是一样的,对于HEAD请求的回应部分来说,它的HTTP头部中包含的信息与通过GET请求所得到的信息是相同的。利用这个方法,不必传输整个资源内容,就可以得到Request-URI所标识的资源的信息。该方法常用于测试超链接的有效性,是否可以访问,以及最近是否更新。 
2、请求报头后述 
3、请求正文(略) 
三、HTTP协议详解之响应篇 
    在接收和解释请求消息后,服务器返回一个HTTP响应消息。 
HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文 
1、状态行格式如下: 
HTTP-Version Status-Code Reason-Phrase CRLF 
其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。 
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值: 
1xx:指示信息--表示请求已接收,继续处理 
2xx:成功--表示请求已被成功接收、理解、接受 
3xx:重定向--要完成请求必须进行更进一步的操作 
4xx:客户端错误--请求有语法错误或请求无法实现 
5xx:服务器端错误--服务器未能实现合法的请求 
常见状态代码、状态描述、说明: 
200 OK      //客户端请求成功 
400 Bad Request  //客户端请求有语法错误,不能被服务器所理解 
401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 
403 Forbidden  //服务器收到请求,但是拒绝提供服务 
404 Not Found  //请求资源不存在,eg:输入了错误的URL 
500 Internal Server Error //服务器发生不可预期的错误 
503 Server Unavailable  //服务器当前不能处理客户端的请求,一段时间后可能恢复正常 
eg:HTTP/1.1 200 OK (CRLF) 
2、响应报头后述 
3、响应正文就是服务器返回的资源的内容 
四、HTTP协议详解之消息报头篇 
    HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。 
HTTP消息报头包括普通报头、请求报头、响应报头、实体报头。 
每一个报头域都是由名字+“:”+空格+值 组成,消息报头域的名字是大小写无关的。 
1、普通报头 
在普通报头中,有少数报头域用于所有的请求和响应消息,但并不用于被传输的实体,只用于传输的消息。 
eg: 
Cache-Control   用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制),HTTP1.0使用的类似的报头域为Pragma。 
请求时的缓存指令包括: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、s-maxage. 
eg:为了指示IE浏览器(客户端)不要缓存页面,服务器端的JSP程序可以编写如下:response.sehHeader("Cache-Control","no-cache"); 
//response.setHeader("Pragma","no-cache");作用相当于上述代码,通常两者//合用 
这句代码将在发送的响应消息中设置普通报头域:Cache-Control:no-cache 
Date普通报头域表示消息产生的日期和时间 
Connection普通报头域允许发送指定连接的选项。例如指定连接是连续,或者指定“close”选项,通知服务器,在响应完成后,关闭连接 
2、请求报头 
请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。 
常用的请求报头 
Accept 
Accept请求报头域用于指定客户端接受哪些类型的信息。eg:Accept:image/gif,表明客户端希望接受GIF图象格式的资源;Accept:text/html,表明客户端希望接受html文本。 
Accept-Charset 
Accept-Charset请求报头域用于指定客户端接受的字符集。eg:Accept-Charset:iso-8859-1,gb2312.如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。 
Accept-Encoding 
Accept-Encoding请求报头域类似于Accept,但是它是用于指定可接受的内容编码。eg:Accept-Encoding:gzip.deflate.如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。 
Accept-Language 
Accept-Language请求报头域类似于Accept,但是它是用于指定一种自然语言。eg:Accept-Language:zh-cn.如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。 
Authorization 
Authorization请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。 
Host(发送请求时,该报头域是必需的) 
Host请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的,eg: 
我们在浏览器中输入:http://www.guet.edu.cn/index.html 
浏览器发送的请求消息中,就会包含Host请求报头域,如下: 
Host:www.guet.edu.cn 
此处使用缺省端口号80,若指定了端口号,则变成:Host:www.guet.edu.cn:指定端口号 
User-Agent 
我们上网登陆论坛的时候,往往会看到一些欢迎信息,其中列出了你的操作系统的名称和版本,你所使用的浏览器的名称和版本,这往往让很多人感到很神奇,实际上,服务器应用程序就是从User-Agent这个请求报头域中获取到这些信息。User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。不过,这个报头域不是必需的,如果我们自己编写一个浏览器,不使用User-Agent请求报头域,那么服务器端就无法得知我们的信息了。 


我们来看一下网上对HTTP 头:Last-Modified 与 If-Modified-Since的介绍。(看了两个博客,都没有图片。)


简 单的说,Last-Modified 与If-Modified-Since 都是用于记录页面最后修改时间的 HTTP 头信息,只是 Last-Modified 是由服务器往客户端发送的 HTTP 头,而 If-Modified-Since 则是由客户端往服务器发送的头,可 以看到,再次请求本地存在的 cache 页面时,客户端会通过 If-Modified-Since 头将先前服务器端发过来的 Last-Modified 最后修改时间戳发送回去,这是为了让服务器端进行验证,通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回新的内容,如果是最新的,则 返回 304 告诉客户端其本地 cache 的页面是最新的,于是客户端就可以直接从本地加载页面了,这样在网络上传输的数据就会大大减少,同时也减轻了服务器的负担。想要详细查看 HTTP 头信息,可以在 Firefox 中安装 LiveHTTPHeaders 插件,安装完成之后按 Alt+L 就可以在 Sidebar 中看到了。




请求报头举例: 
GET /form.html HTTP/1.1 (CRLF) 
Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/* (CRLF) 
Accept-Language:zh-cn (CRLF) 
Accept-Encoding:gzip,deflate (CRLF) 
If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF) 
If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF) 
User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF) 
Host:www.guet.edu.cn (CRLF) 
Connection:Keep-Alive (CRLF) 
(CRLF) 
3、响应报头 
响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息。 
常用的响应报头 
Location 
Location响应报头域用于重定向接受者到一个新的位置。Location响应报头域常用在更换域名的时候。 
Server 
Server响应报头域包含了服务器用来处理请求的软件信息。与User-Agent请求报头域是相对应的。下面是 
Server响应报头域的一个例子: 
Server:Apache-Coyote/1.1 
WWW-Authenticate 
WWW-Authenticate响应报头域必须被包含在401(未授权的)响应消息中,客户端收到401响应消息时候,并发送Authorization报头域请求服务器对其进行验证时,服务端响应报头就包含该报头域。 
eg:WWW-Authenticate:Basic realm="Basic Auth Test!"  //可以看出服务器对请求资源采用的是基本验证机制。 
4、实体报头 
请求和响应消息都可以传送一个实体。一个实体由实体报头域和实体正文组成,但并不是说实体报头域和实体正文要在一起发送,可以只发送实体报头域。实体报头定义了关于实体正文(eg:有无实体正文)和请求所标识的资源的元信息。 
常用的实体报头 
Content-Encoding 
Content-Encoding实体报头域被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得 Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。Content-Encoding这样用于记录文档的压缩方法,eg:Content-Encoding:gzip 
Content-Language 
Content-Language实体报头域描述了资源所用的自然语言。没有设置该域则认为实体内容将提供给所有的语言阅读 
者。eg:Content-Language:da 
Content-Length 
Content-Length实体报头域用于指明实体正文的长度,以字节方式存储的十进制数字来表示。 
Content-Type 
Content-Type实体报头域用语指明发送给接收者的实体正文的媒体类型。eg: 
Content-Type:text/html;charset=ISO-8859-1 
Content-Type:text/html;charset=GB2312 
Last-Modified 
Last-Modified实体报头域用于指示资源的最后修改日期和时间。 
Expires 
Expires实体报头域给出响应过期的日期和时间。为了让代理服务器或浏览器在一段时间以后更新缓存中(再次访问曾访问过的页面时,直接从缓存中加载,缩短响应时间和降低服务器负载)的页面,我们可以使用Expires实体报头域指定页面过期的时间。eg:Expires:Thu,15 Sep 2006 16:23:12 GMT 
HTTP1.1的客户端和缓存必须将其他非法的日期格式(包括0)看作已经过期。eg:为了让浏览器不要缓存页面,我们也可以利用Expires实体报头域,设置为0,jsp中程序如下:response.setDateHeader("Expires","0"); 
五、利用telnet观察http协议的通讯过程 
    实验目的及原理: 
    利用MS的telnet工具,通过手动输入http请求信息的方式,向服务器发出请求,服务器接收、解释和接受请求后,会返回一个响应,该响应会在telnet窗口上显示出来,从而从感性上加深对http协议的通讯过程的认识。 
    实验步骤: 
1、打开telnet 
1.1 打开telnet 
运行-->cmd-->telnet 
1.2 打开telnet回显功能 
set localecho 
2、连接服务器并发送请求 
2.1 open www.guet.edu.cn 80  //注意端口号不能省略 
    HEAD /index.asp HTTP/1.0 
    Host:www.guet.edu.cn 
   /*我们可以变换请求方法,请求桂林电子主页内容,输入消息如下*/ 
    open www.guet.edu.cn 80 
    GET /index.asp HTTP/1.0  //请求资源的内容 
    Host:www.guet.edu.cn  
2.2 open www.sina.com.cn 80  //在命令提示符号下直接输入telnet www.sina.com.cn 80 
    HEAD /index.asp HTTP/1.0 
    Host:www.sina.com.cn 
3 实验结果: 
3.1 请求信息2.1得到的响应是: 
HTTP/1.1 200 OK                                              //请求成功 
Server: Microsoft-IIS/5.0                                    //web服务器 
Date: Thu,08 Mar 200707:17:51 GMT 
Connection: Keep-Alive                                 
Content-Length: 23330 
Content-Type: text/html 
Expries: Thu,08 Mar 2007 07:16:51 GMT 
Set-Cookie: ASPSESSIONIDQAQBQQQB=BEJCDGKADEDJKLKKAJEOIMMH; path=/ 
Cache-control: private 
//资源内容省略 
3.2 请求信息2.2得到的响应是: 
HTTP/1.0 404 Not Found       //请求失败 
Date: Thu, 08 Mar 2007 07:50:50 GMT 
Server: Apache/2.0.54 <Unix> 
Last-Modified: Thu, 30 Nov 2006 11:35:41 GMT 
ETag: "6277a-415-e7c76980" 
Accept-Ranges: bytes 
X-Powered-By: mod_xlayout_jh/0.0.1vhs.markII.remix 
Vary: Accept-Encoding 
Content-Type: text/html 
X-Cache: MISS from zjm152-78.sina.com.cn 
Via: 1.0 zjm152-78.sina.com.cn:80<squid/2.6.STABLES-20061207> 
X-Cache: MISS from th-143.sina.com.cn 
Connection: close 
失去了跟主机的连接 
按任意键继续... 
4 .注意事项:1、出现输入错误,则请求不会成功。 
          2、报头域不分大小写。 
          3、更深一步了解HTTP协议,可以查看RFC2616,在http://www.letf.org/rfc上找到该文件。 
          4、开发后台程序必须掌握http协议 
六、HTTP协议相关技术补充 
    1、基础: 
    高层协议有:文件传输协议FTP、电子邮件传输协议SMTP、域名系统服务DNS、网络新闻传输协议NNTP和HTTP协议等 
中介由三种:代理(Proxy)、网关(Gateway)和通道(Tunnel),一个代理根据URI的绝对格式来接受请求,重写全部或部分消息,通过 URI的标识把已格式化过的请求发送到服务器。网关是一个接收代理,作为一些其它服务器的上层,并且如果必须的话,可以把请求翻译给下层的服务器协议。一个通道作为不改变消息的两个连接之间的中继点。当通讯需要通过一个中介(例如:防火墙等)或者是中介不能识别消息的内容时,通道经常被使用。 
     代理(Proxy):一个中间程序,它可以充当一个服务器,也可以充当一个客户机,为其它客户机建立请求。请求是通过可能的翻译在内部或经过传递到其它的服务器中。一个代理在发送请求信息之前,必须解释并且如果可能重写它。代理经常作为通过防火墙的客户机端的门户,代理还可以作为一个帮助应用来通过协议处理没有被用户代理完成的请求。 
网关(Gateway):一个作为其它服务器中间媒介的服务器。与代理不同的是,网关接受请求就好象对被请求的资源来说它就是源服务器;发出请求的客户机并没有意识到它在同网关打交道。 
网关经常作为通过防火墙的服务器端的门户,网关还可以作为一个协议翻译器以便存取那些存储在非HTTP系统中的资源。 
    通道(Tunnel):是作为两个连接中继的中介程序。一旦激活,通道便被认为不属于HTTP通讯,尽管通道可能是被一个HTTP请求初始化的。当被中继的连接两端关闭时,通道便消失。当一个门户(Portal)必须存在或中介(Intermediary)不能解释中继的通讯时通道被经常使用。 
2、协议分析的优势—HTTP分析器检测网络攻击 
以模块化的方式对高层协议进行分析处理,将是未来入侵检测的方向。 
HTTP及其代理的常用端口80、3128和8080在network部分用port标签进行了规定 
3、HTTP协议Content Lenth限制漏洞导致拒绝服务攻击 
使用POST方法时,可以设置ContentLenth来定义需要传送的数据长度,例如ContentLenth:999999999,在传送完成前,内 存不会释放,攻击者可以利用这个缺陷,连续向WEB服务器发送垃圾数据直至WEB服务器内存耗尽。这种攻击方法基本不会留下痕迹。 
http://www.cnpaf.net/Class/HTTP/0532918532667330.html 
4、利用HTTP协议的特性进行拒绝服务攻击的一些构思 
服务器端忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求(毕竟客户端的正常请求比率非常之小),此时从正常客户的角度看来,服务器失去响应,这种情况我们称作:服务器端受到了SYNFlood攻击(SYN洪水攻击)。 
而Smurf、TearDrop等是利用ICMP报文来Flood和IP碎片攻击的。本文用“正常连接”的方法来产生拒绝服务攻击。 
19端口在早期已经有人用来做Chargen攻击了,即Chargen_Denial_of_Service,但是!他们用的方法是在两台 Chargen 服务器之间产生UDP连接,让服务器处理过多信息而DOWN掉,那么,干掉一台WEB服务器的条件就必须有2个:1.有Chargen服务2.有HTTP 服务 
方法:攻击者伪造源IP给N台Chargen发送连接请求(Connect),Chargen接收到连接后就会返回每秒72字节的字符流(实际上根据网络实际情况,这个速度更快)给服务器。 
5、Http指纹识别技术 
   Http指纹识别的原理大致上也是相同的:记录不同服务器对Http协议执行中的微小差别进行识别.Http指纹识别比TCP/IP堆栈指纹识别复杂许多,理由是定制Http服务器的配置文件、增加插件或组件使得更改Http的响应信息变的很容易,这样使得识别变的困难;然而定制TCP/IP堆栈的行为需要对核心层进行修改,所以就容易识别. 
      要让服务器返回不同的Banner信息的设置是很简单的,象Apache这样的开放源代码的Http服务器,用户可以在源代码里修改Banner信息,然后重起Http服务就生效了;对于没有公开源代码的Http服务器比如微软的IIS或者是Netscape,可以在存放Banner信息的Dll文件中修改,相关的文章有讨论的,这里不再赘述,当然这样的修改的效果还是不错的.另外一种模糊Banner信息的方法是使用插件。 
常用测试请求: 
1:HEAD/Http/1.0发送基本的Http请求 
2:DELETE/Http/1.0发送那些不被允许的请求,比如Delete请求 
3:GET/Http/3.0发送一个非法版本的Http协议请求 
4:GET/JUNK/1.0发送一个不正确规格的Http协议请求 
Http指纹识别工具Httprint,它通过运用统计学原理,组合模糊的逻辑学技术,能很有效的确定Http服务器的类型.它可以被用来收集和分析不同Http服务器产生的签名。 
6、其他:为了提高用户使用浏览器时的性能,现代浏览器还支持并发的访问方式,浏览一个网页时同时建立多个连接,以迅速获得一个网页上的多个图标,这样能更快速完成整个网页的传输。 
HTTP1.1中提供了这种持续连接的方式,而下一代HTTP协议:HTTP-NG更增加了有关会话控制、丰富的内容协商等方式的支持,来提供 
更高效率的连接。






文件下载原理详解 http协议2009年10月20日 星期二 16:23于HTTP协议,向服务器请求某个文件时,只要发送类似如下的请求即可: 
     GET /Path/FileName HTTP/1.0


Host: www.server.com:80


Accept: */*


User-Agent: GeneralDownloadApplication


Connection: close


     每行用一个“回车换行”分隔,末尾再追加一个“回车换行”作为整个请求的结束。


第一行中的GET是HTTP协议支持的方法之一,方法名是大小写敏感的,HTTP协议还支持OPTIONS、HAED、POST、PUT、DELETE、TRACE、CONNECT等方法,而GET和HEAD这两个方法通常被认为是“安全的”,也就是说任何实现了HTTP协议的服务器程序都会实现这两个方法。对于文件下载功能,GET足矣。GET后面是一个空格,其后紧跟的是要下载的文件从WEB服务器根开始的绝对路径。该路径后又有一个空格,然后是协议名称及协议版本。


除第一行以外,其余行都是HTTP头的字段部分。Host字段表示主机名和端口号,如果端口号是默认的80则可以不写。Accept字段中的*/*表示接收任何类型的数据。User-Agent表示用户代理,这个字段可有可无,但强烈建议加上,因为它是服务器统计、追踪以及识别客户端的依据。Connection字段中的close表示使用非持久连接。


关于HTTP协议更多的细节可以参考RFC2616(HTTP 1.1)。因为我只是想通过HTTP协议实现文件下载,所以也只看了一部分,并没有看全。


如果服务器成功收到该请求,并且没有出现任何错误,则会返回类似下面的数据:


HTTP/1.0 200 OK


Content-Length: 13057672


Content-Type: application/octet-stream


Last-Modified: Wed, 10 Oct 2005 00:56:34 GMT


Accept-Ranges: bytes


ETag: "2f38a6cac7cec51:160c"


Server: Microsoft-IIS/6.0


X-Powered-By: ASP.NET


Date: Wed, 16 Nov 2005 01:57:54 GMT


Connection: close


不用逐一解释,很多东西一看几乎就明白了,只说我们大家都关心内容吧。


第一行是协议名称及版本号,空格后面会有一个三位数的数字,是HTTP协议的响应状态码,200表示成功,OK是对状态码的简短文字描述。状态码共有5类:1xx属于通知类;2xx属于成功类;3xx属于重定向类;4xx属于客户端错误类;5xx属于服务端错误类。对于状态码,相信大家对404应该很熟悉,如果向一个服务器请求一个不存在的文件,就会得到该错误,通常浏览器也会显示类似“HTTP 404 - 未找到文件”这样的错误。Content-Length字段是一个比较重要的字段,它标明了服务器返回数据的长度,这个长度是不包含HTTP头长度的。换句话说,我们的请求中并没有Range字段(后面会说到),表示我们请求的是整个文件,所以Content-Length就是整个文件的大小。其余各字段是一些关于文件和服务器的属性信息。


这段返回数据同样是以最后一行的结束标志(回车换行)和一个额外的回车换行作为结束,即“\r\n\r\n”。而“\r\n\r\n”后面紧接的就是文件的内容了,这样我们就可以找到“\r\n\r\n”,并从它后面的第一个字节开始,源源不断的读取,再写到文件中了。


以上就是通过HTTP协议实现文件下载的全过程。但还不能实现断点续传,而实际上断点续传的实现非常简单,只要在请求中加一个Range字段就可以了。


假如一个文件有1000个字节,那么其范围就是0-999,则:


Range: bytes=500-      表示读取该文件的500-999字节,共500字节。 
Range: bytes=500-599   表示读取该文件的500-599字节,共100字节。 
Range还有其它几种写法,但上面这两种是最常用的,对于断点续传也足矣了。如果HTTP请求中包含Range字段,那么服务器会返回206(Partial Content),同时HTTP头中也会有一个相应的Content-Range字段,类似下面的格式: 
Content-Range: bytes 500-999/1000 
Content-Range字段说明服务器返回了文件的某个范围及文件的总长度。这时Content-Length字段就不是整个文件的大小了,而是对应文件这个范围的字节数,这一点一定要注意。


一切好像基本上没有什么问题了,本来我也是这么认为的,但事实并非如此。如果我们请求的文件的URL是类似http://www.server.com/filename.exe这样的文件,则不会有问题。但是很多软件下载网站的文件下载链接都是通过程序重定向的,比如pchome的ACDSee的HTTP下载地址是:


http://download.pchome.net/php/tdownload2.php?sid=5547&url=/multimedia/viewer/acdc31sr1b051007.exe&svr=1&typ=0


这种地址并没有直接标识文件的位置,而是通过程序进行了重定向。如果向服务器请求这样的URL,服务器就会返回302(Moved Temporarily),意思就是需要重定向,同时在HTTP头中会包含一个Location字段,Location字段的值就是重定向后的目的URL。这时就需要断开当前的连接,而向这个重定向后的服务器发请求。


     好了,原理基本上就是这些了。其实装个Sniffer好好分析一下,很容易就可以分析出来的。不过NetAnts也帮了我一些忙,它的文件下载日志对开发人员还是很有帮助的。
 












网络三名词详解——HTTP协议,session,cookie 
 
作者:佚名    文章来源:本站原创    点击数:1490    更新时间:2007-11-17     
 
http,session,cookie一定是困扰了大家许久了的三个词吧。


下面跟我来。


HTTP Session
一、浅析
HTTP协议(http://www.w3.org/Protocols/)是“一次性单向”协议。


服务端不能主动连接客户端,只能被动等待并答复客户端请求。客户端连接服务端,发出一个HTTP Request,服务端处理请求,并且返回一个HTTP Response给客户端,本次HTTP Request-Response Cycle结束。


我们看到,HTTP协议本身并不能支持服务端保存客户端的状态信息。于是,Web Server中引入了session的概念,用来保存客户端的状态信息。


这里用一个形象的比喻来解释session的工作方式。假设Web Server是一个商场的存包处,HTTP Request是一个顾客,第一次来到存包处,管理员把顾客的物品存放在某一个柜子里面(这个柜子就相当于Session),然后把一个号码牌交给这个顾客,作为取包凭证(这个号码牌就是Session ID)。顾客(HTTP Request)下一次来的时候,就要把号码牌(Session ID)交给存包处(Web Server)的管理员。管理员根据号码牌(Session ID)找到相应的柜子(Session),根据顾客(HTTP Request)的请求,Web Server可以取出、更换、添加柜子(Session)中的物品,Web Server也可以让顾客(HTTP Request)的号码牌和号码牌对应的柜子(Session)失效。顾客(HTTP Request)的忘性很大,管理员在顾客回去的时候(HTTP Response)都要重新提醒顾客记住自己的号码牌(Session ID)。这样,顾客(HTTP Request)下次来的时候,就又带着号码牌回来了。


Session ID实际上是在客户端和服务端之间通过HTTP Request和HTTP Response传来传去的。号码牌(Session ID)必须包含在HTTP Request里面。关于HTTP Request的具体格式,请参见HTTP协议(http://www.w3.org/Protocols/)。这里只做一个简单的介绍。


在Java Web Server(即Servlet/JSP Server)中,Session ID用jsessionid表示(请参见Servlet规范)。


HTTP Request一般由3部分组成:


(1)Request Line


这一行由HTTP Method(如GET或POST)、URL、和HTTP版本号组成。


例如,GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1


GET http://www.google.com/search?q=Tomcat HTTP/1.1


POST http://www.google.com/search HTTP/1.1


GET http://www.somsite.com/menu.do;jsessionid=1001 HTTP/1.1


 


(2)Request Headers


这部分定义了一些重要的头部信息,如,浏览器的种类,语言,类型。Request Headers中还可以包括Cookie的定义。例如:


User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)


Accept-Language: en-us


Cookie: jsessionid=1001


 


(3)Message Body


如果HTTP Method是GET,那么Message Body为空。


如果HTTP Method是POST,说明这个HTTP Request是submit一个HTML Form的结果,


那么Message Body为HTML Form里面定义的Input属性。例如,


user=guest


password=guest


jsessionid=1001


主意,如果把HTML Form元素的Method属性改为GET。那么,Message Body为空,所有的Input属性都会加在URL的后面。你在浏览器的URL地址栏中会看到这些属性,类似于


http://www.somesite/login.do?user=guest&password=guest&jsessionid=1001


 


从理论上来说,这3个部分(Request URL,Cookie Header, Message Body)都可以用来存放Session ID。由于Message Body方法必须需要一个包含Session ID的HTML Form,所以这种方法不通用。


一般用来实现Session的方法有两种:


(1)URL重写。


Web Server在返回Response的时候,检查页面中所有的URL,包括所有的连接,和HTML Form的Action属性,在这些URL后面加上“;jsessionid=XXX”。


下一次,用户访问这个页面中的URL。jsessionid就会传回到Web Server。


(2)Cookie。


如果客户端支持Cookie,Web Server在返回Response的时候,在Response的Header部分,加入一个“set-cookie: jsessionid=XXXX”header属性,把jsessionid放在Cookie里传到客户端。


客户端会把Cookie存放在本地文件里,下一次访问Web Server的时候,再把Cookie的信息放到HTTP Request的“Cookie”header属性里面,这样jsessionid就随着HTTP Request返回给Web Server。


二、相关资料
前面是我自作聪明的一段个人浅见,下面我来找点权威资料支持。


http://www.w3.org/Protocols/


 


Use of HTTP State Management (RFC 2964). 


ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt


 


这个文件是定义“HTTP State Management”扩展协议部分。里面有这么一段,


It's important to realize that similar capabilities may also be    achieved using the "bare" HTTP protocol, and/or dynamically-generated 


HTML, without the State Management extensions.  For example, state information can be transmitted from the service to the user by    embedding a session identifier in one or more URLs which appear in HTTP redirects, or dynamically generated HTML; and the state information may be returned from the user to the service when such URLs appear in a GET or POST request.  HTML forms can also be used to pass state information from the service to the user and back, without the user being aware of this happening.


 
这段话的意思是说,不用这个 “HTTP State Management”扩展协议部分,我们也可以用“纯粹”的HTTP协议实现Session -- 比如URL重写,HTML Form等。


这里面没有提到Cookie。因为“HTTP State Management” 扩展协议部分本身包括了关于Cookie的内容。这一点可以通过


HTTP State Management Mechanism (RFC 2965), 


ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt 


看出来。


 


STATE AND SESSIONS


This document describes a way to create stateful sessions with HTTP    requests and responses.  Currently, HTTP servers respond to each    client request without relating that request to previous or    subsequent requests; the state management mechanism allows clients    and servers that wish to exchange state information to place HTTP    requests and responses within a larger context, which we term a    "session".  This context might be used to create, for example, a    "shopping cart", in which user selections can be aggregated before    purchase, or a magazine browsing system, in which a user's previous    reading affects which offerings are presented.


Neither clients nor servers are required to support cookies.  A    server MAY refuse to provide content to a client that does not return    the cookies it sends.


 


后面还给出了例子(其中的汉语部分是我加的)。


 


4.1  Example 1


Most detail of request and response headers has been omitted.  Assume


the user agent has no stored cookies.


 


      1. User Agent -> Server


 


        POST /acme/login HTTP/1.1


        [form data]


 


        User identifies self via a form.


 


      2. Server -> User Agent


 


        HTTP/1.1 200 OK


        Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"


 


        Cookie reflects user's identity.


(这里面的 Customer="WILE_E_COYOTE" 应该就是从Form Data里面来的,这时候又传回给了客户端)


 


      3. User Agent -> Server


 


        POST /acme/pickitem HTTP/1.1


        Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"


        [form data]


 


        User selects an item for "shopping basket".


 


      4. Server -> User Agent


 


        HTTP/1.1 200 OK


        Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";


                Path="/acme"


 


        Shopping basket contains an item.


(这个火箭发射器显然也是从Form Data来的。但为什么Part_Number="Rocket_Launcher_0001"也需要传回给客户端?


Customer="WILE_E_COYOTE"; 应该是在传统的”Set-Cookie”里面传回给客户端的。” Set-Cookie2” 的作用应该是向Cookie里面添加东西。)


 


      5. User Agent -> Server


 


        POST /acme/shipping HTTP/1.1


        Cookie: $Version="1";


                Customer="WILE_E_COYOTE"; $Path="/acme";


                Part_Number="Rocket_Launcher_0001"; $Path="/acme"


        [form data]


 


        User selects shipping method from form.


 


(客户传给服务器的Cookie里面包括了Customer和Part_Number)


 


      6. Server -> User Agent


 


        HTTP/1.1 200 OK


        Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"


 


        New cookie reflects shipping method.


 


      7. User Agent -> Server


 


        POST /acme/process HTTP/1.1


        Cookie: $Version="1";


                Customer="WILE_E_COYOTE"; $Path="/acme";


                Part_Number="Rocket_Launcher_0001"; $Path="/acme";


                Shipping="FedEx"; $Path="/acme"


        [form data]


 


 


        User chooses to process order.


 


      8. Server -> User Agent


 


        HTTP/1.1 200 OK


 


        Transaction is complete.


 


   The user agent makes a series of requests on the origin server, after each of which it receives a new cookie.  All the cookies have the same Path attribute and (default) domain.  Because the request-URIs all path-match /acme, the Path attribute of each cookie, each request contains all the cookies received so far.


(看到这里,我才大致明白,原来那个$Path="/acme" 大致起着 JSessionID的作用)


 


三、Tomcat5的HTTP Session实现
下面我们来看Tomcat5的源代码如何支持HTTP 1.1 Session。


我们可以用jsessionid, Set-Cookie等关键字搜索Tomcat5源代码。


 


首先,我们来看常量定义:


org.apache.catalina.Globals


 


    /**


     * The name of the cookie used to pass the session identifier back


     * and forth with the client.


     */


    public static final String SESSION_COOKIE_NAME = "JSESSIONID";


 


 


    /**


     * The name of the path parameter used to pass the session identifier


     * back and forth with the client.


     */


public static final String SESSION_PARAMETER_NAME = "jsessionid";


 


Cookie里面用大写的JSESSIONID,URL后缀用的是小写的jsessionid。


Session的具体实现类是org.apache.catalina.session.StandardSession。一个Tomcat Server的所有Session都由一个Manager(拥有一个Context)统一管理。我估计有一个 Session Listener 专门管理Cluster之间的Session数据复制,具体的我没有追查下去。


 


另外几个重要的相关类是org.apache.coyote.tomcat5包下面的CoyoteRequest , CoyoteResponse, CoyoteAdapter三个类。


 


org.apache.coyote.tomcat5.CoyoteResponse类的toEncoded()方法支持URL重写。


String toEncoded(String url, String sessionId) {





        StringBuffer sb = new StringBuffer(path);


        if( sb.length() > 0 ) { // jsessionid can't be first.


            sb.append(";jsessionid=");


            sb.append(sessionId);


        }


        sb.append(anchor);


        sb.append(query);


        return (sb.toString());


}


 


我们来看org.apache.coyote.tomcat5.CoyoteRequest的两个方法configureSessionCookie()


doGetSession()用Cookie支持jsessionid.


 


    /**


     * Configures the given JSESSIONID cookie.


     *


     * @param cookie The JSESSIONID cookie to be configured


     */


    protected void configureSessionCookie(Cookie cookie) {


       …


    }


 


    HttpSession doGetSession(boolean create){


      …


        // Creating a new session cookie based on that session


        if ((session != null) && (getContext() != null)


               && getContext().getCookies()) {


            Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,


                                       session.getId());


            configureSessionCookie(cookie);


            ((HttpServletResponse) response).addCookie(cookie);


        }


      …


    }


 


四、More
HTTP Session的协议、规范、实现大概就是这些。


另外,在frameset中使用Session的情况有些复杂,不同环境表现可能不同。


其余相关的概念有“单点登录”(Sing Sign On – SSO), Domain, Cluster, Proxy, Cache等。
 
常见文件http中content-type头值-(MIME类型)
.ppt – application/mspowerpoint
.ai – application/postscript
.aif – audio/x-aiff
.aifc – audio/x-aiff
.aiff – audio/x-aiff
.asc – text/plain
.au – audio/basic
.avi – video/x-msvideo
.bcpio – application/x-bcpio
.bin – application/octet-stream
.c – text/plain
.cc – text/plain


.ccad – application/clariscad
.cdf – application/x-netcdf
.class – application/octet-stream
.cpio – application/x-cpio
.cpt – application/mac-compactpro
.csh – application/x-csh
.css – text/css
.dcr – application/x-director
.dir – application/x-director
.dms – application/octet-stream
.doc – application/msword
.drw – application/drafting
.dvi – application/x-dvi
.dwg – application/acad
.dxf – application/dxf
.dxr – application/x-director
.eps – application/postscript
.etx – text/x-setext
.exe – application/octet-stream
.ez – application/andrew-inset
.f – text/plain
.f90 – text/plain
.fli – video/x-fli
.gif – image/gif
.gtar – application/x-gtar
.gz – application/x-gzip
.h – text/plain
.hdf – application/x-hdf
.hh – text/plain
.hqx – application/mac-binhex40
.htm – text/html
.html – text/html
.ice – x-conference/x-cooltalk
.ief – image/ief
.iges – model/iges
.igs – model/iges
.ips – application/x-ipscript
.ipx – application/x-ipix
.jpe – image/jpeg
.jpeg – image/jpeg
.jpg – image/jpeg
.js – application/x-javascript
.kar – audio/midi
.latex – application/x-latex
.lha – application/octet-stream
.lsp – application/x-lisp
.lzh – application/octet-stream
.m – text/plain
.man – application/x-troff-man
.me – application/x-troff-me
.mesh – model/mesh
.mid – audio/midi
.midi – audio/midi
.mif – application/vnd.mif
.mime – www/mime
.mov – video/quicktime
.movie – video/x-sgi-movie
.mp2 – audio/mpeg
.mp3 – audio/mpeg
.mpe – video/mpeg
.mpeg – video/mpeg
.mpg – video/mpeg
.mpga – audio/mpeg
.ms – application/x-troff-ms
.msh – model/mesh
.nc – application/x-netcdf
.oda – application/oda
.pbm – image/x-portable-bitmap
.pdb – chemical/x-pdb
.pdf – application/pdf
.pgm – image/x-portable-graymap
.pgn – application/x-chess-pgn
.png – image/png
.pnm – image/x-portable-anymap
.pot – application/mspowerpoint
.ppm – image/x-portable-pixmap
.pps – application/mspowerpoint
.ppt – application/mspowerpoint
.ppz – application/mspowerpoint
.pre – application/x-freelance
.prt – application/pro_eng
.ps – application/postscript
.qt – video/quicktime
.ra – audio/x-realaudio
.ram – audio/x-pn-realaudio
.ras – image/cmu-raster
.rgb – image/x-rgb
.rm – audio/x-pn-realaudio
.roff – application/x-troff
.rpm – audio/x-pn-realaudio-plugin
.rtf – text/rtf
.rtx – text/richtext
.scm – application/x-lotusscreencam
.set – application/set
.sgm – text/sgml
.sgml – text/sgml
.sh – application/x-sh
.shar – application/x-shar
.silo – model/mesh
.sit – application/x-stuffit
.skd – application/x-koan
.skm – application/x-koan
.skp – application/x-koan
.skt – application/x-koan
.smi – application/smil
.smil – application/smil
.snd – audio/basic
.sol – application/solids
.spl – application/x-futuresplash
.src – application/x-wais-source
.step – application/STEP
.stl – application/SLA
.stp – application/STEP
.sv4cpio – application/x-sv4cpio
.sv4crc – application/x-sv4crc
.swf – application/x-shockwave-flash
.t – application/x-troff
.tar – application/x-tar
.tcl – application/x-tcl
.tex – application/x-tex
.texi – application/x-texinfo
.texinfo – application/x-texinfo
.tif – image/tiff
.tiff – image/tiff
.tr – application/x-troff
.tsi – audio/TSP-audio
.tsp – application/dsptype
.tsv – text/tab-separated-values
.txt – text/plain
.unv – application/i-deas
.ustar – application/x-ustar
.vcd – application/x-cdlink
.vda – application/vda
.viv – video/vnd.vivo
.vivo – video/vnd.vivo
.vrml – model/vrml
.wav – audio/x-wav
.wrl – model/vrml
.xbm – image/x-xbitmap
.xlc – application/vnd.ms-excel
.xll – application/vnd.ms-excel
.xlm – application/vnd.ms-excel
.xls – application/vnd.ms-excel
.xlw – application/vnd.ms-excel
.xml – text/xml
.xpm – image/x-xpixmap
.xwd – image/x-xwindowdump
.xyz – chemical/x-pdb
.zip – application/zip




http状态码的含义:
1**:请求收到,继续处理
2**:操作成功收到,分析、接受
3**:完成此请求必须进一步处理
4**:请求包含一个错误语法或不能完成
5**:服务器执行一个完全有效请求失败
100——客户必须继续发出请求
101——客户要求服务器根据请求转换HTTP协议版本
200——交易成功
201——提示知道新文件的URL
202——接受和处理、但处理未完成
203——返回信息不确定或不完整
204——请求收到,但返回信息为空
205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件
206——服务器已经完成了部分用户的GET请求
300——请求的资源可在多处得到
301——删除请求数据
302——在其他地址发现了请求数据
303——建议客户访问其他URL或访问方式
304——客户端已经执行了GET,但文件未变化
305——请求的资源必须从服务器指定的地址得到
306——前一版本HTTP中使用的代码,现行版本中不再使用
307——申明请求的资源临时性删除
400——错误请求,如语法错误
401——请求授权失败
402——保留有效ChargeTo头响应
403——请求不允许
404——没有发现文件、查询或URl
405——用户在Request-Line字段定义的方法不允许
406——根据用户发送的Accept拖,请求资源不可访问
407——类似401,用户必须首先在代理服务器上得到授权
408——客户端没有在用户指定的饿时间内完成请求
409——对当前资源状态,请求不能完成
410——服务器上不再有此资源且无进一步的参考地址
411——服务器拒绝用户定义的Content-Length属性请求
412——一个或多个请求头字段在当前请求中错误
413——请求的资源大于服务器允许的大小
414——请求的资源URL长于服务器允许的长度
415——请求资源不支持请求项目格式
416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求
也不包含If-Range请求头字段
417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下
一级服务器不能满足请求
500——服务器产生内部错误
501——服务器不支持请求的函数
502——服务器暂时不可用,有时是为了防止发生系统过载
503——服务器过载或暂停维修
504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长
505——服务器不支持或拒绝支请求头中指定的HTTP版本


查看文章    
php多线程(转)2009-07-09 01:16php多线程2008-07-18 17:45最近研究php多线程的问题,发现中文资源少的可怜,仅有的几篇文章被转了又转,但文中内容价值有限。搜索过程中发现国外很多网站引用的一篇文章写的不错,所以翻译过来。


版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
http://blog.iyi.cn/start/


问题:
有没有办法在php中实现多线程呢?


假设你正在写一个基于多台服务器的php应用,理想的情况时同时向多台服务器发送请求,而不是一台接一台。
可以实现吗?
回答:


当有人想要实现并发功能时,他们通常会想到用fork或者spawn threads,但是当他们发现php不支持多线程的时候,大概会转换思路去用一些不够好的语言,比如perl。


其实的是大多数情况下,你大可不必使用fork或者线程,并且你会得到比用fork或thread更好的性能。


假设你要建立一个服务来检查正在运行的n台服务器,以确定他们还在正常运转。你可能会写下面这样的代码:


<?php
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com");
$timeout = 15;
$status = array();
foreach ($hosts as $host) {
$errno = 0;
$errstr = "";
$s = fsockopen($host, 80, $errno, $errstr, $timeout);
if ($s) {
   $status[$host] = "Connectedn";
   fwrite($s, "HEAD / HTTP/1.0rnHost: $hostrnrn");
   do {
    $data = fread($s, 8192);
    if (strlen($data) == 0) {
    break;
    }
    $status[$host] .= $data;
   } while (true);
   fclose($s);
} else {
   $status[$host] = "Connection failed: $errno $errstrn";
}
}
print_r($status);
?>


它运行的很好,但是在fsockopen()分析完hostname并且建立一个成功的连接(或者延时$timeout秒)之前,扩充这段代码来管理大量服务器将耗费很长时间。
因此我们必须放弃这段代码;我们可以建立异步连接-不需要等待fsockopen返回连接状态。PHP仍然需要解析hostname(所以直接使用ip更加明智),不过将在打开一个连接之后立刻返回,继而我们就可以连接下一台服务器。
有两种方法可以实现;PHP5中可以使用新增的stream_socket_client()函数直接替换掉fsocketopen()。PHP5之前的版本,你需要自己动手,用sockets扩展解决问题。


下面是PHP5中的解决方法:
<?php
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com");
$timeout = 15;
$status = array();
$sockets = array();
/* Initiate connections to all the hosts simultaneously */
foreach ($hosts as $id => $host) {
$s = stream_socket_client("$host:80", $errno, $errstr, $timeout,
   STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
if ($s) {
   $sockets[$id] = $s;
   $status[$id] = "in progress";
} else {
   $status[$id] = "failed, $errno $errstr";
}
}
/* Now, wait for the results to come back in */
while (count($sockets)) {
$read = $write = $sockets;
/* This is the magic function - explained below */
$n = stream_select($read, $write, $e = null, $timeout);
if ($n > 0) {
   /* readable sockets either have data for us, or are failed
   * connection attempts */
   foreach ($read as $r) {
      $id = array_search($r, $sockets);
      $data = fread($r, 8192);
      if (strlen($data) == 0) {
    if ($status[$id] == "in progress") {
     $status[$id] = "failed to connect";
    }
    fclose($r);
    unset($sockets[$id]);
      } else {
    $status[$id] .= $data;
      }
   }
   /* writeable sockets can accept an HTTP request */
   foreach ($write as $w) {
    $id = array_search($w, $sockets);
    fwrite($w, "HEAD / HTTP/1.0rnHost: "
     . $hosts[$id] . "rnrn");
    $status[$id] = "waiting for response";
   }
} else {
   /* timed out waiting; assume that all hosts associated
   * with $sockets are faulty */
   foreach ($sockets as $id => $s) {
    $status[$id] = "timed out " . $status[$id];
   }
   break;
}
}
foreach ($hosts as $id => $host) {
echo "Host: $hostn";
echo "Status: " . $status[$id] . "nn";
}


?>


我们用stream_select()等待sockets打开的连接事件。stream_select()调用系统的select(2)函数来工作:前面三个参数是你要使用的streams的数组;你可以对其读取,写入和获取异常(分别针对三个参数)。stream_select()可以通过设置$timeout(秒)参数来等待事件发生-事件发生时,相应的sockets数据将写入你传入的参数。


下面是PHP4.1.0之后版本的实现,如果你已经在编译PHP时包含了sockets(ext/sockets)支持,你可以使用根上面类似的代码,只是需要将上面的streams/filesystem函数的功能用ext/sockets函数实现。主要的不同在于我们用下面的函数代替stream_socket_client()来建立连接:
<?php
// This value is correct for Linux, other systems have other values
define('EINPROGRESS', 115);
function non_blocking_connect($host, $port, &$errno, &$errstr, $timeout) {
$ip = gethostbyname($host);
$s = socket_create(AF_INET, SOCK_STREAM, 0);
if (socket_set_nonblock($s)) {
   $r = @socket_connect($s, $ip, $port);
   if ($r || socket_last_error() == EINPROGRESS) {
    $errno = EINPROGRESS;
    return $s;
   }
}
$errno = socket_last_error($s);
$errstr = socket_strerror($errno);
socket_close($s);
return false;
}
?>


现在用socket_select()替换掉stream_select(),用socket_read()替换掉fread(),用socket_write()替换掉fwrite(),用socket_close()替换掉fclose()就可以执行脚本了!
PHP5的先进之处在于,你可以用stream_select()处理几乎所有的stream-例如你可以通过include STDIN用它接收键盘输入并保存进数组,你还可以接收通过proc_open()打开的管道中的数据。
如果你想让PHP4.3.x自身拥有处理streams的功能,我已经为你准备了一个让fsockopen可以异步工作的patch。不赞成使用该补丁,该补丁不会出现在官方发布的PHP版本中,我在补丁中附带了stream_socket_client()函数的实现,通过它,你可以让你的脚本兼容PHP5。
附件:
documentation for stream_select()
documentation for socket_select()
patch for PHP 4.3.2 and script to emulate stream_socket_client(). (might work with later 4.3.x versions).


------------------------------------


经测试,确实为多线程,弄了整个下午,终于弄好了~~~




$request = array("http://10.1.30.218/test/server.php","http://10.1.30.28/server.php");


foreach($request as $r) {
$temp = parse_url($r);
$scheme[] = $temp['scheme'];
$hosts[] = $temp['host'];
$paths[] = isset($temp['path']) ? $temp['path'] : "" ;
}


//$hosts = array("www.bit.edu.cn");


$timeout = 5;
$status = array();
$sockets = array();
// Initiate connections to all the hosts simultaneously 
foreach ($hosts as $id => $host) {
$s = stream_socket_client("$host:80", $errno, $errstr, $timeout, STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
if ($s) {
   $sockets[$id] = $s;
   $status[$id] = "in progress";
} else {
   $status[$id] = "failed, $errno $errstr";
}
}
//print_r($sockets);print_r($status);
//*
// Now, wait for the results to come back in 
while (count($sockets)) {
$read = $write = $sockets;
// This is the magic function - explained below 
$ret = stream_select($read, $write, $e = null, $timeout);
if ($ret > 0) {
   // readable sockets either have data for us, or are failed connection attempts 
   foreach ($read as $r) {
      $id = array_search($r, $sockets);
    $data = fread($r, 8192);
    if (strlen($data) == 0) {
     if ($status[$id] == "in progress") {
      $status[$id] = "failed to connect";
     }
     fclose($r);
     unset($sockets[$id]);
    } else {
     if ($status[$id] == "in progress") {
      $status[$id] = $data;
     } else {
      $status[$id] .= $data;
     }
    }
   }
   // writeable sockets can accept an HTTP request
   foreach ($write as $w) {
    $id = array_search($w, $sockets);
    //fwrite($w, "HEAD / HTTP/1.0\r\nHost: " . $hosts[$id] . "\r\n\r\n");
    fwrite($w, "GET /".$paths[$id]." HTTP/1.0\r\nHost: " . $hosts[$id] . "\r\n\r\n");
    //$status[$id] = "waiting for response";
   }


} else {
   // timed out waiting; assume that all hosts associated with $sockets are faulty 
   foreach ($sockets as $id => $s) {
    $status[$id] = "timed out\r\n\r\n" . $status[$id];
   }
   break;
}
}
foreach ($hosts as $id => $host) {
echo "Host: $host\n";
echo '<pre>'.$status[$id].'</pre>';
/*
$pos = strpos($status[$id],"\r\n\r\n");
$content[$id] = substr($status[$id],$pos);
$status[$id] = substr($status[$id],0,$pos);
//echo "Status: " . $status[$id] . "\n\n";
echo $content[$id] . "\n\n" ;
*/
}
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值