以前讲过http的协议,怎么约定请求或响应的行、头、体,也介绍怎么使用curl来完成http的请求。这一次,再接再砺,换一个角度换一些角色,再次说http的封装与使用。反正目的只有一个:加深对http协议的理解。php
说http的实现,非讲tcp不可(为何?后面会解释),而以前讲tcp协议的使用,用到了socket,用socket就能通讯,就能作tcp想作的事情。html
那socket是什么东西?java
用你的话来理解,socket是一个通道吗?由于它能通讯啊。“socket是一个通道”已是抓到重点了,虽然不完整,由于socket在成为通道前,并非通道,它只是一个等待使用的值。可是,“突出重点”的抽象是如此重要,不要追求完整或完美。socket,是网络结点(ip:port),能够表明数据通道,经过它就能够在结点间传送数据。 能够想象,网络上的一切数据交换,都由socket完成。python
socket并不仅是一个抽象的概念,它仍是实实在在的代码实现,但你能看到的是二进制代码,它已经给别人实现了,并且存在于各个平台,好比windows、unix、ios、android等,那它在这些平台是什么样的存在?它又是谁实现的呢?android
结合wiki等网页的信息:ios
套接字起源于20世纪70年代加州大学伯克利分校版本的Unix,因此,这个套接字称为“伯克利套接字”或“BSD套接字”。 BSD sockets,是全部其它平台的socket的祖先,包括windows/liunx,还有某些语言使用的socket好比python、java等。bsd socket用c语言实现,有对应的c层静态库(或动态库)。web
windows的socket是Martin Hall、Mark Towfiq(从属于Microsoft)等人的版权,有两个大版本,即winsock1跟winsock2,对应的库文件是wsock32.lib/ws2_32.lib,你在windows平台上就是在使用这个库文件进行socket操做。apache
好了,具体某些平台是谁写了socket的实现,就不深究了,只是说,感谢前辈们的贡献。windows
那问题来了,socket实现了什么?浏览器
socket实现了tcp协议(还有udp),这个很重要,对于http来讲也很重要,由于http在协议栈上位于应用层,依赖于传输层的tcp协议,也就是说,http最终要用tcp来传输数据,而socket包括了tcp的实现,你说重不重要?
因此,http协议依靠socket来传输数据,但依靠还依靠,不表明http要理socket层的事情。
http跟tcp不一样,http并不关心数据怎么传来传去,反正能传就行,这个是tcp层的事情,那http还要考虑什么?就是请求与响应的格式啊,并且是人能读懂的格式,http的要求已经很人性化了,这个对话已经很友好了(不像tcp的数据)。http报文的格式以前介绍了的!
对于http请求或响应数据,包括行、头、体,其中行与头是少不了的。那行头体的格式由谁来封装,注意,这不是socket的事情,socket无论这个,那谁管?
工具curl管http格式封装(也管数据收发),java的HttpClient管http格式封装,python的urllib也管http格式封装,还有不少工具类或库都能封装http协议(注意,说http就只有http协议,不包括tcp,由于tcp是另外一层的事情),固然,你本身写一个http的格式封装,也没有问题。
简单来讲,http的实现其实并不包括怎么传输的事情,那是tcp层(socket)的事情,http的实现重点在于自己协议的封装与实现,好比怎么组装请求头、怎么调用接口收发数据、怎么解释数据、GET或POST方式封装、url编码,等等。你会发现有不少零散的知识点,不少实现还跟业务关系密切,这也是http是应用层协议的表现。
对于你,如无必要,优先考虑已经稳定的http封装实现吧,具体看你是什么平台进行对应的选择。
以前讲http的结构跟怎么使用curl时,其实就是经过curl使用了http。是的,咱们并不会直接“使用http”,而是经过工具类来使用http,你能够本身写工具类好比弄一个httpclient的类出来,也可使用一些表现良好的现有的工具类,建议用后者。
这一次,我用python库的一些工具,好比urllib或httplib,经过它们来使用http协议。
通常使用http,就是要向服务器发出请求,那服务器怎么来?你天然能够找一堆服务器出来,只要有对应的url就能够,实际上你也只是请求某个url。可是,为了显得高级一点,我这里自行定制一个web服务器,用来接收http请求。
如何定制web服务器?
小程在介绍“PHP程序”的使用时,把apache给引了进来,apache是一个web服务器,能够接受你的http请求。另外一方面,apache能够跟PHP解析器、python解析器等进行交互,能够根据http请求,执行一些脚本代码(像php脚本或python脚本等)。
这里就演示一下,怎么发起一个http请求到apache,apache再执行python脚本,返回http响应。
按以前讲解的知识,apache项目的目录是:/Library/WebServer/Documents,以前php脚本就是放在这里。可是,对于python脚本,解释它的是cgi,这些脚本要放在cgi项目目录,那个目录是:/Library/WebServer/CGI-Executables,因此你写的python脚本要放在这个目录。 先无论那么多,在CGI-Executables目录下,建立一个hello.py,随便写点什么,好比:
#!/usr/bin/python # -*- coding: UTF-8 -*- title="小程" info="你好,first py script,关注微信公众号'广州小程'" htmlcontent="<html><head><meta charset='utf-8'><title>%s</title></head><body><h2>%s</h2></body></html>" % (title,info) print("Content-type:text/html") print("") print(htmlcontent)
而后打开浏览器来请求一下这个文件吧(注意请求cgi-bin目录),结果是这样的:
显然,这不是我想看到呀,我不是要看脚本代码,我是想看到脚本代码被解释后的内容!
若是客户端好比浏览器,向apache请求了python或php脚本,而apache没有找到对应的脚本解释器的话(注意apache本身是不懂脚本解释的),apache会把这个脚本的内容完整地返回给浏览器,这时源码暴露无遗,这极可能不是你想看到的事情。
因此,让apache找到脚本解释器(这里是cgi),是相当重要的一步。
再也不讲apache的启用了,直接讲,怎么样才能让apache执行python脚本呢?你可能还记得,让apache调用php程序的话,要把apache的配置文件httpd.conf改一下(/private/etc/apache2目录下),也就是“LoadModule phpx_module...”这一行解除注释,就能够调用php程序了,那对于python是否是也这样改呢?是的,bingo,也要解除注释,这一句是:
#LoadModule cgi_module libexec/apache2/mod_cgi.so
把前面的#去掉并保存一下便可,注意重启一下apache:
sudo apachectl restart
这时,apache就会加载mod_cgi.so,并用cgi来解释python脚本。再用浏览器请求一次,看到这样的返回:
简单来讲,cgi就是用来解释python脚本的,apache启用它就解决了python脚本解释的问题,更多cgi的东西不说了。
至此,apache已经能够执行我写的python脚本了,那我写这个hello.py来作什么呢?固然是处理浏览器发过来的http请求了。因此,把hello.py再修改一下,让它根据请求的参数值,返回不一样的内容给浏览器,好比这样:
#!/usr/bin/python # -*- coding: UTF-8 -*- import cgi title="广州小程" info="" paras = cgi.FieldStorage() if paras.has_key('secret'): if paras['secret'].value=='free': info="<h1>welcome, you are so clever! secret is %s</h1>" % paras['secret'].value else: info="<h1>wrong! secret is NOT '%s'</h>" % paras['secret'].value else: info="<h1>NO! 请加上secret参数!</h1>" htmlcontent="<html><head><meta charset='utf-8'><title>%s</title></head><body>%s</body></html>" % (title,info) print("Content-type:text/html") print("")
脚本中使用cgi.FieldStorage获取请求参数,对这个参数给出了不一样的提示。用浏览器请求一下,传递不一样的参数,能够看这样的展现:
至此,一切都是美好的,服务器脚本定好了,能应对请求了,浏览器也看到返回内容了,但是,我不是教你怎么作web服务啊,我想说的是,怎么使用http协议的呀!
这个才是重点内容。这里演示写一个客户端,用python里面的类,完成http请求,以表示,我已经会使用http协议了!
分别用urllib2跟httplib来演示。
import urllib2 url='http://localhost/cgi-bin/hello.py?secret=free' req=urllib2.Request(url) res=urllib2.urlopen(req) res=res.read() print(res)
执行上面的代码,使用urllib2这个http协议封装类,发起请求,返回是这样的:
import httplib url='http://localhost/cgi-bin/hello.py?xx' conn=httplib.HTTPConnection('localhost') conn.request(method='GET', url=url) res=conn.getresponse() res=res.read() conn.close() print(res)
这一段代码就形象不少了,先用HTTPConnection创建起连接(指定ip跟端口,端口省略意思就是用默认的80即web服务器的默认端口,另外个人localhost是固定到本机的),而后发起request,再取响应,一鼓作气。执行结果是这样的:
以上请求都是get方式,post也是很容易的,你研究一下嘛。好了,已经会用http协议了,至少是用上了。
总结一下吧,本文主要讲怎么使用http协议,为了说明白这个操做,讲了tcp跟http的大道理,还讲了怎么让apache解释python脚本,最后那一点演示才是重点,你至少知道有urllib这样的类可使用http协议了吧!