文档章节

Python学习之HTTP模块

 天使爱美
发布于 2016/11/08 16:45
字数 2582
阅读 14
收藏 0
点赞 0
评论 0

我学习的习惯,除了学习如何使用,还喜欢研究源码,学习运行机制,这样用起来才会得心应手或者说,使用这些语言或框架,就和平时吃饭睡觉一样,非常自然;因为最近有接触到bottle和flask web框架,所以想看下这两个的源码,但是这两个框架是基于Python语言自带的http,因此就有了这篇文章;

python http简单例子

python http框架主要有server和handler组成,server主要是用于建立网络模型,例如利用epoll监听socket;handler用于处理各个就绪的socket;先来看下python http简单的使用:

import sys

from http.server import HTTPServer,SimpleHTTPRequestHandler

ServerClass = HTTPServer

HandlerClass = SimpleHTTPRequestHandler

if__name__ =='__main__':

port = int(sys.argv[2])

server_address = (sys.argv[1],port)

httpd = ServerClass(server_address,HandlerClass)

sa=httpd.socket.getsockname()print("Serving HTTP on",sa[0],"port",sa[1],"...")

try:

httpd.serve_forever()

except KeyboardInterrupt:print("\nKeyboard interrupt received, exiting.")

httpd.server_close()

sys.exit(0)

  运行上述例子,可以得到如下:

python3 myhttp.py 127.0.0.1 9999

  此时如果在当前文件夹新建一个index.html文件,就可以通过 http://127.0.0.1:9999/index.html 访问了index.html页面了。

  这个例子的server类用的是HTTPServer,handler类是SimpleHTTPRequestHandler,因此当HTTPServer监听到有request到来时,就把这个request丢给SimpleHTTPRequestHandler类求处理;ok,了解这些之后,我们开始分别分析下server和handler.

http之server

http模块的设计充分利用了面向对象的继承多态,因为之前有看了会tfs文件系统的代码,所以再看python http时,没那么大的压力;先给出server的继承关系

+------------------+

+------------+| tcpserver基类 |

| BaseServer +-------->| 开启事件循环监听 |

+-----+------+ | 处理客户端请求 |

| +------------------+

v +-----------------+

+------------+| httpserver基类 |

| TCPServer +-------->+设置监听socket |

+-----+------+ | 开启监听 |

| +-----------------+

v

+------------+

| HTTPServer |

+------------+

  继承关系如上图所示,其中BaseServer和TCPServer在文件socketserver.py,HTTPServer在http/server.py;我们先看下来BaseServer;

BaseServer

  因为BaseServer是所有server的基类,因此BaseServer尽可能抽象出所有server的共性,例如开启事件监听循环,这就是每个server的共性,因此这也是BaseServer主要做的使;我们来看下BaseServer主要代码部分

defserve_forever(self, poll_interval=0.5):

self.__is_shut_down.clear()try:

with_ServerSelector()asselector:

selector.register(self, selectors.EVENT_READ)

whilenotself.__shutdown_request:

ready = selector.select(poll_interval)

ifready:

self._handle_request_noblock()

self.service_actions()finally:

self.__shutdown_request = False

self.__is_shut_down.set()

  代码中的selector其实就是封装了select,poll,epoll等的io多路复用,然后将服务自身监听的socket注册到io多路复用,开启事件监听,当有客户端连接时,此时会调用self._handle_request_noblock()来处理请求;接下来看下这个处理函数做了啥;

def_handle_request_noblock(self):try:

request, client_address = self.get_request()

exceptOSError:return

ifself.verify_request(request, client_address):try:

self.process_request(request, client_address)

except:

self.handle_error(request, client_address)

self.shutdown_request(request)else:

self.shutdown_request(request)

_handle_request_noblock函数是一个内部函数,首先是接收客户端连接请求,底层其实是封装了系统调用accept函数,然后验证请求,最后调用process_request来处理请求;其中get_request是属于子类的方法,因为tcp和udp接收客户端请求是不一样的(tcp有连接,udp无连接)

  我们接下来再看下process_request具体做了什么;

defprocess_request(self, request, client_address):

self.finish_request(request, client_address)

self.shutdown_request(request)# -------------------------------------------------

deffinish_request(self, request, client_address):

self.RequestHandlerClass(request, client_address, self)

defshutdown_request(self, request):

self.close_request(request)

process_request函数先是调用了finish_request来处理一个连接,处理结束之后,调用shutdown_request函数来关闭这个连接;而finish_request函数内部实例化了一个handler类,并把客户端的socket和地址传了进去,说明,handler类在初始化结束的时候,就完成了请求处理,这个等后续分析handler时再细看;

  以上就是BaseServer所做的事,这个BaseServer不能直接使用,因为有些函数还没实现,只是作为tcp/udp的抽象层;总结下:

1. 先是调用serve_forever开启事件监听;

2. 然后当有客户端请求到来时,将请求交给handler处理;

TCPServer

  由上述BaseServer抽象出的功能,我们可以知道TCPServer或UDPServer应该完成的功能有,初始化监听套接字,并绑定监听,最后当有客户端请求时,接收这个客户端;我们来看下代码

BaseServer==>

def__init__(self, server_address, RequestHandlerClass):"""Constructor. May be extended, do not override."""

self.server_address = server_address

self.RequestHandlerClass = RequestHandlerClass

self.__is_shut_down = threading.Event()

self.__shutdown_request = False#--------------------------------------------------------------------------------

TCPServer==>

def__init__(self, server_address, RequestHandlerClass, bind_and_activate=True):

BaseServer.__init__(self, server_address, RequestHandlerClass)

self.socket = socket.socket(self.address_family,

self.socket_type)

ifbind_and_activate:try:

self.server_bind()

self.server_activate()

except:

self.server_close()

raise

TCPServer初始化时先是调用基类BaseServer的初始化函数,初始化服务器地址,handler类等,然后初始化自身的监听套接字,最后调用server_bind绑定套接字,server_activate监听套接字

defserver_bind(self):

ifself.allow_reuse_address:

self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

self.socket.bind(self.server_address)

self.server_address = self.socket.getsockname()

defserver_activate(self):

self.socket.listen(self.request_queue_size)

TCPServer还实现了另一个函数,那就是接收客户端请求,

defget_request(self):returnself.socket.accept()

  之前如果有学过linux编程,那么看这些代码应该会觉得很熟悉,因为函数名和Linux提供的系统调用名一模一样,这里也不多说了;

TCPServer其实已经把基于tcp的服务器主体框架搭起来了,因此HTTPServer在继承TCPServer基础上,只是重载了server_bind函数,设置reuse_address等;

ok,这里分析下上述例子程序的开启过程;

1. httpd = ServerClass(server_address,HandlerClass)这行代码在初始化HTTPServer时,主要是调用基类TCPServer的初始化方法,初始化了监听的套接字,并绑定和监听;

2. httpd.serve_forever()这行代码调用的是基类BaseServer的serve_forever方法,开启监听循环,等待客户端的连接;

  如果有看过redis或者一些后台组件的源码,对这种并发模型应该很熟悉;ok,分析了server之后,接下来看下handler是如何处理客户端请求的。

http之handler

handler类主要分析tcp层的handler和http应用层的handler,tcp层的handler是不能使用的,因为tcp层只负责传输字节,但是并不知对于接收到的字节要如何解析,如何处理等;因此应用层协议如该要使用TCP协议,必须继承TCP handler,然后实现handle函数即可;例如,http层的handler实现handle函数,解析http协议,处理业务请求以及结果返回给客户端;先来看下tcp层的handler

tcp层handler

tcp层handler主要有BaseRequestHandler和StreamRequestHandler(都在socketserver.py文件),先看下BaseRequestHandler代码,

classBaseRequestHandler:

def__init__(self, request, client_address, server):

self.request = request

self.client_address = client_address

self.server = server

self.setup()try:

self.handle()finally:

self.finish()

defsetup(self):

pass

defhandle(self):

pass

deffinish(self):

pass

  之前在看server时,知道处理客户端请求就是在handler类的初始化函数中完成;由这个基类初始化函数,我们知道处理请求大概经历三个过程:

1. setup对客户端的socket做一些设置;

2. handle真正处理请求的函数;

3. finish关闭socket读写请求;

  这个BaseRequestHandler是handler top level 基类,只是抽象出handler整体框架,并没有实际的处理;我们看下tcp handler,

classStreamRequestHandler(BaseRequestHandler):

timeout = None

disable_nagle_algorithm = False

defsetup(self):

self.connection = self.request

ifself.timeoutisnotNone:

self.connection.settimeout(self.timeout)

ifself.disable_nagle_algorithm:

self.connection.setsockopt(socket.IPPROTO_TCP,

socket.TCP_NODELAY, True)

self.rfile = self.connection.makefile('rb', self.rbufsize)

self.wfile = self.connection.makefile('wb', self.wbufsize)

deffinish(self):

ifnotself.wfile.closed:try:

self.wfile.flush()

exceptsocket.error:

pass

self.wfile.close()

self.rfile.close()

tcp handler实现了setup和finish函数,setup函数设置超时时间,开启nagle算法以及设置socket读写缓存;finish函数关闭socket读写;

  由上述两个tcp层的handler可知,要实现一个基于http的服务器handler,只需要继承StreamRequestHandler类,并实现handle函数即可;因此这也是http层handler主要做的事;

http层handler

  由之前tcp层handler的介绍,我们知道http层handler在继承tcp层handler基础上,主要是实现了handle函数处理客户端的请求;还是直接看代码吧;

defhandle(self):

self.close_connection = True

self.handle_one_request()

whilenotself.close_connection:

self.handle_one_request()

  这就是BaseHTTPRequestHandler的handle函数,在handle函数会调用handle_one_request函数处理一次请求;默认情况下是短链接,因此在执行了一次请求之后,就不会进入while循环在同一个连接上处理下一个请求,但是在handle_one_request函数内部会进行判断,如果请求头中的connection为keep_alive或者http版本大于等于1.1,则可以保持长链接;接下来看下handle_one_request函数是如何处理;

defhandle_one_request(self):try:self.raw_requestline =self.rfile.readline(65537)

iflen(self.raw_requestline) >65536:self.requestline =''self.request_version =''self.command =''self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)return

ifnotself.raw_requestline:self.close_connection = Truereturn

ifnotself.parse_request():return

mname = 'do_'+self.command

ifnothasattr(self, mname):self.send_error(

HTTPStatus.NOT_IMPLEMENTED,"Unsupported method (%r)"%self.command)return

method = getattr(self, mname)

method()self.wfile.flush()

except socket.timeout as e:self.log_error("Request timed out: %r", e)self.close_connection = Truereturn

  这个handle_one_request执行过程如下:

1. 先是调用parse_request解析客户端http请求内容

2. 通过"do_"+command构造出请求所对于的函数method

3. 调用method函数,处理业务并将response返回给客户端

  这个BaseHTTPRequestHandler是http handler基类,因此也是无法直接使用,因为它没有定义请求处理函数,即method函数;好在python为我们提供了一个简单的SimpleHTTPRequestHandler,该类继承了BaseHTTPRequestHandler,并实现了请求函数;我们看下get函数:

# SimpleHTTPRequestHandler# ---------------------------------------------

defdo_GET(self):"""Serve a GET request."""

f = self.send_head()

iff:try:

self.copyfile(f, self.wfile)finally:

f.close()

  这个get函数先是调用do_GET函数给客户端返回response头部,并返回请求的文件,最后调用copyfile函数将请求文件通过连接返回给客户端;

  以上就是http模块最基础的内容,最后,总结下例子程序handler部分:

1. server把请求传给SimpleHTTPRequestHandler初始化函数;

2. SimpleHTTPRequestHandler在初始化部分,对这个客户端connection进行一些设置;

3. 接着调用handle函数处理请求;

4. 在handle函数接着调用handle_one_request处理请求;

5. 在handle_one_request函数内部,解析请求,找到请求处理函数;

6. 我之前的访问属于get访问,因此直接调用do_GET函数将index.html文件返回给客户端;

python http模块到此已经分析结束;不知道大家有没发现,python自带的http模块使用起来不是很方便,因为它是通过请求方法来调用请求函数,这样当同一方法调用次数非常多时,例如get和post方法,会导致这个请求函数异常庞大,代码不好编写,各种情况判断;当然SimpleHTTPRequestHandler只是python提供的一个简单例子而已;

  当然,python官方提供了针对http更好用的框架,即wsgi server和wsgi application;接下来文章先分析python自带的wsgiref模块以及bottle,后面再分析flask;

 

文章来源:罗道文的私房菜

© 著作权归作者所有

共有 人打赏支持
粉丝 0
博文 28
码字总数 53872
作品 0
朝阳
python-35:urllib 和 urllib2 模块

首先,urllib 和 urllib2 是python 自带的模块,所以我们可以通过官网文档来查看它们的详细信息,这里依然给出网址,感兴趣的同学可以看一下 urllib模块:http://python.usyiyi.cn/python278...

达岭凹老大 ⋅ 2015/12/01 ⋅ 0

Python爬取拉勾网数据分析职位

python文件打开方式详解——a、a+、r+、w+区别 Python模块学习 - openpyxl 1.信息获取,所需工具:拉勾网、Python3。 原来课程地址:python拉勾网爬虫 反爬:伪造浏览器请求 'User-Agent':'...

sinat_23880167 ⋅ 05/27 ⋅ 0

156个Python网络爬虫资源,妈妈再也不用担心你找不到资源了

本列表包含Python网页抓取和数据处理相关的库。 前几天有私信小编要Python的学习资料,小编整理了一些有深度的Python教程和参考资料,从入门到高级的都有,文件已经打包好了,正在学习Pytho...

雁横 ⋅ 05/02 ⋅ 0

老司机工作总结!零基础python小白学习编程一定要知道的那些技巧

今天又是一年一度的关键时刻:高考了。高考可谓是牵动着无数普通家庭的心。但是高考就是千军万马过独木桥,是一场没有硝烟的战争,有输有赢。可是高考跟战争不一样的是,高考就算失败也会有其...

Python学习 ⋅ 06/07 ⋅ 0

比较基础的urllib库来了解一下

什么是urllib库 Python内置的HTTP请求库 urllib.request 请求模块 urllib.error 异常处理模块 urllib.parse url解析模块 urllib.robotparser robots.txt解析模块 相比Python2的变化 在Pytho...

爱吃西瓜的番茄酱 ⋅ 05/30 ⋅ 0

程序员必备,快速学习 Python 的全套14张思维导图(附高清版下载)

后台回复关键词 思维导图 可获取本文中的高清思维导图(PDF版) ML & AI∣一个有用的公众号 长按,识别二维码,加关注 获取更多精彩文章

micf435p6d221ssdld2 ⋅ 05/23 ⋅ 0

[python应用案例] 一.BeautifulSoup爬取天气信息并发送至QQ邮箱

前面作者写了很多Python系列文章,包括: Python基础知识系列:Python基础知识学习与提升 Python网络爬虫系列:Python爬虫之Selenium+Phantomjs+CasperJS Python数据分析系列:知识图谱、web...

eastmount ⋅ 05/03 ⋅ 0

连 00 后学 Python 了,身为程序员的我慌了

2018 年 1 月 16 日,教育部召开新闻发布会,正式将人工智能、物联网、大数据处理划入高中新课标,并于今年秋季开始执行。这意味着,现在 16 岁的学生就要开始学习编程了! 本次核心目标:修...

dqcfkyqdxym3f8rb0 ⋅ 05/06 ⋅ 0

Python学习你掌握对方法了吗?python自动化

阅读官方文档即可满足日常需求,官方文档有中文翻译,更加方便学习。但这些都是基础的语法和常见的模块,Python学习重要的是模块,快速、高效的开发依赖的是模块的应用,站在前人的肩膀会省时...

老男孩Linux培训 ⋅ 06/12 ⋅ 0

为什么Python成了开发AI的主流语言?Java系列、Ruby等编程语言不行吗?

导读 说道开发AI的语言,很多人说,不是Python吗?没错,Python是可以开发AI,但是不知道你们有没有想过,真的就只有Python才可以开发AI吗? 其实,像Java、Ruby也是开发AI很好的选择,但是为...

柯西带你学编程 ⋅ 05/29 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 58分钟前 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

Dockerfile基础命令总结

FROM 指定使用的基础base image FROM scratch # 制作base image ,不使用任何基础imageFROM centos # 使用base imageFROM ubuntu:14.04 尽量使用官方的base image,为了安全 LABEL 描述作...

ExtreU ⋅ 昨天 ⋅ 0

存储,对比私有云和公有云的不同

导读 说起公共存储,很难不与后网络公司时代的选择性外包联系起来,但尽管如此,它还是具备着简单和固有的可用性。公共存储的名字听起来也缺乏专有性,很像是把东西直接堆放在那里而不会得到...

问题终结者 ⋅ 昨天 ⋅ 0

C++难点解析之const修饰符

C++难点解析之const修饰符 c++ 相比于其他编程语言,可能是最为难掌握,概念最为复杂的。结合自己平时的C++使用经验,这里将会列举出一些常见的难点并给出相应的解释。 const修饰符 const在c...

jackie8tao ⋅ 昨天 ⋅ 0

聊聊spring cloud netflix的HystrixCommands

序 本文主要研究一下spring cloud netflix的HystrixCommands。 maven <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-clo......

go4it ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部