99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

Python HTTP模塊詳解

發(fā)布時(shí)間:2016-11-08 16:45  回復(fù):0  查看:2523   最后回復(fù):2016-11-08 16:45  

我學(xué)習(xí)的習(xí)慣,除了學(xué)習(xí)如何使用,還喜歡研究源碼,學(xué)習(xí)運(yùn)行機(jī)制,這樣用起來才會(huì)得心應(yīng)手或者說,使用這些語言或框架,就和平時(shí)吃飯睡覺一樣,非常自然;因?yàn)樽罱薪佑|到bottleflask web框架,所以想看下這兩個(gè)的源碼,但是這兩個(gè)框架是基于Python語言自帶的http,因此就有了這篇文章;

python http簡(jiǎn)單例子

python http框架主要有serverhandler組成,server主要是用于建立網(wǎng)絡(luò)模型,例如利用epoll監(jiān)聽sockethandler用于處理各個(gè)就緒的socket;先來看下python http簡(jiǎn)單的使用:

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)

  運(yùn)行上述例子,可以得到如下:

python3 myhttp.py 127.0.0.1 9999

  此時(shí)如果在當(dāng)前文件夾新建一個(gè)index.html文件,就可以通過 http://127.0.0.1:9999/index.html 訪問了index.html頁面了。

  這個(gè)例子的server類用的是HTTPServer,handler類是SimpleHTTPRequestHandler,因此當(dāng)HTTPServer監(jiān)聽到有request到來時(shí),就把這個(gè)request丟給SimpleHTTPRequestHandler類求處理;ok,了解這些之后,我們開始分別分析下serverhandler.

httpserver

http模塊的設(shè)計(jì)充分利用了面向?qū)ο蟮睦^承多態(tài),因?yàn)橹坝锌戳藭?huì)tfs文件系統(tǒng)的代碼,所以再看python http時(shí),沒那么大的壓力;先給出server的繼承關(guān)系

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

+------------+| tcpserver基類 |

| BaseServer +-------->| 開啟事件循環(huán)監(jiān)聽 |

+-----+------+ | 處理客戶端請(qǐng)求 |

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

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

+------------+| httpserver基類 |

| TCPServer +-------->+設(shè)置監(jiān)聽socket |

+-----+------+ | 開啟監(jiān)聽 |

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

v

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

| HTTPServer |

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

  繼承關(guān)系如上圖所示,其中BaseServerTCPServer在文件socketserver.py,HTTPServerhttp/server.py;我們先看下來BaseServer;

BaseServer

  因?yàn)?/span>BaseServer是所有server的基類,因此BaseServer盡可能抽象出所有server的共性,例如開啟事件監(jiān)聽循環(huán),這就是每個(gè)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其實(shí)就是封裝了select,poll,epoll等的io多路復(fù)用,然后將服務(wù)自身監(jiān)聽的socket注冊(cè)到io多路復(fù)用,開啟事件監(jiān)聽,當(dāng)有客戶端連接時(shí),此時(shí)會(huì)調(diào)用self._handle_request_noblock()來處理請(qǐng)求;接下來看下這個(gè)處理函數(shù)做了啥;

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函數(shù)是一個(gè)內(nèi)部函數(shù),首先是接收客戶端連接請(qǐng)求,底層其實(shí)是封裝了系統(tǒng)調(diào)用accept函數(shù),然后驗(yàn)證請(qǐng)求,最后調(diào)用process_request來處理請(qǐng)求;其中get_request是屬于子類的方法,因?yàn)?/span>tcpudp接收客戶端請(qǐng)求是不一樣的(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函數(shù)先是調(diào)用了finish_request來處理一個(gè)連接,處理結(jié)束之后,調(diào)用shutdown_request函數(shù)來關(guān)閉這個(gè)連接;而finish_request函數(shù)內(nèi)部實(shí)例化了一個(gè)handler類,并把客戶端的socket和地址傳了進(jìn)去,說明,handler類在初始化結(jié)束的時(shí)候,就完成了請(qǐng)求處理,這個(gè)等后續(xù)分析handler時(shí)再細(xì)看;

  以上就是BaseServer所做的事,這個(gè)BaseServer不能直接使用,因?yàn)橛行┖瘮?shù)還沒實(shí)現(xiàn),只是作為tcp/udp的抽象層;總結(jié)下:

1. 先是調(diào)用serve_forever開啟事件監(jiān)聽;

2. 然后當(dāng)有客戶端請(qǐng)求到來時(shí),將請(qǐng)求交給handler處理;

TCPServer

  由上述BaseServer抽象出的功能,我們可以知道TCPServerUDPServer應(yīng)該完成的功能有,初始化監(jiān)聽套接字,并綁定監(jiān)聽,最后當(dāng)有客戶端請(qǐng)求時(shí),接收這個(gè)客戶端;我們來看下代碼

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初始化時(shí)先是調(diào)用基類BaseServer的初始化函數(shù),初始化服務(wù)器地址,handler類等,然后初始化自身的監(jiān)聽套接字,最后調(diào)用server_bind綁定套接字,server_activate監(jiān)聽套接字

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還實(shí)現(xiàn)了另一個(gè)函數(shù),那就是接收客戶端請(qǐng)求,

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

  之前如果有學(xué)過linux編程,那么看這些代碼應(yīng)該會(huì)覺得很熟悉,因?yàn)楹瘮?shù)名和Linux提供的系統(tǒng)調(diào)用名一模一樣,這里也不多說了;

TCPServer其實(shí)已經(jīng)把基于tcp的服務(wù)器主體框架搭起來了,因此HTTPServer在繼承TCPServer基礎(chǔ)上,只是重載了server_bind函數(shù),設(shè)置reuse_address等;

ok,這里分析下上述例子程序的開啟過程;

1. httpd = ServerClass(server_address,HandlerClass)這行代碼在初始化HTTPServer時(shí),主要是調(diào)用基類TCPServer的初始化方法,初始化了監(jiān)聽的套接字,并綁定和監(jiān)聽;

2. httpd.serve_forever()這行代碼調(diào)用的是基類BaseServerserve_forever方法,開啟監(jiān)聽循環(huán),等待客戶端的連接;

  如果有看過redis或者一些后臺(tái)組件的源碼,對(duì)這種并發(fā)模型應(yīng)該很熟悉;ok,分析了server之后,接下來看下handler是如何處理客戶端請(qǐng)求的。

httphandler

handler類主要分析tcp層的handlerhttp應(yīng)用層的handler,tcp層的handler是不能使用的,因?yàn)?/span>tcp層只負(fù)責(zé)傳輸字節(jié),但是并不知對(duì)于接收到的字節(jié)要如何解析,如何處理等;因此應(yīng)用層協(xié)議如該要使用TCP協(xié)議,必須繼承TCP handler,然后實(shí)現(xiàn)handle函數(shù)即可;例如,http層的handler實(shí)現(xiàn)handle函數(shù),解析http協(xié)議,處理業(yè)務(wù)請(qǐng)求以及結(jié)果返回給客戶端;先來看下tcp層的handler

tcphandler

tcphandler主要有BaseRequestHandlerStreamRequestHandler(都在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時(shí),知道處理客戶端請(qǐng)求就是在handler類的初始化函數(shù)中完成;由這個(gè)基類初始化函數(shù),我們知道處理請(qǐng)求大概經(jīng)歷三個(gè)過程:

1. setup對(duì)客戶端的socket做一些設(shè)置;

2. handle真正處理請(qǐng)求的函數(shù);

3. finish關(guān)閉socket讀寫請(qǐng)求;

  這個(gè)BaseRequestHandlerhandler top level 基類,只是抽象出handler整體框架,并沒有實(shí)際的處理;我們看下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實(shí)現(xiàn)了setupfinish函數(shù),setup函數(shù)設(shè)置超時(shí)時(shí)間,開啟nagle算法以及設(shè)置socket讀寫緩存;finish函數(shù)關(guān)閉socket讀寫;

  由上述兩個(gè)tcp層的handler可知,要實(shí)現(xiàn)一個(gè)基于http的服務(wù)器handler,只需要繼承StreamRequestHandler類,并實(shí)現(xiàn)handle函數(shù)即可;因此這也是httphandler主要做的事;

httphandler

  由之前tcphandler的介紹,我們知道httphandler在繼承tcphandler基礎(chǔ)上,主要是實(shí)現(xiàn)了handle函數(shù)處理客戶端的請(qǐng)求;還是直接看代碼吧;

defhandle(self):

self.close_connection = True

self.handle_one_request()

whilenotself.close_connection:

self.handle_one_request()

  這就是BaseHTTPRequestHandlerhandle函數(shù),在handle函數(shù)會(huì)調(diào)用handle_one_request函數(shù)處理一次請(qǐng)求;默認(rèn)情況下是短鏈接,因此在執(zhí)行了一次請(qǐng)求之后,就不會(huì)進(jìn)入while循環(huán)在同一個(gè)連接上處理下一個(gè)請(qǐng)求,但是在handle_one_request函數(shù)內(nèi)部會(huì)進(jìn)行判斷,如果請(qǐng)求頭中的connectionkeep_alive或者http版本大于等于1.1,則可以保持長(zhǎng)鏈接;接下來看下handle_one_request函數(shù)是如何處理;

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

  這個(gè)handle_one_request執(zhí)行過程如下:

1. 先是調(diào)用parse_request解析客戶端http請(qǐng)求內(nèi)容

2. 通過"do_"+command構(gòu)造出請(qǐng)求所對(duì)于的函數(shù)method

3. 調(diào)用method函數(shù),處理業(yè)務(wù)并將response返回給客戶端

  這個(gè)BaseHTTPRequestHandlerhttp handler基類,因此也是無法直接使用,因?yàn)樗鼪]有定義請(qǐng)求處理函數(shù),即method函數(shù);好在python為我們提供了一個(gè)簡(jiǎn)單的SimpleHTTPRequestHandler,該類繼承了BaseHTTPRequestHandler,并實(shí)現(xiàn)了請(qǐng)求函數(shù);我們看下get函數(shù):

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

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

f = self.send_head()

iff:try:

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

f.close()

  這個(gè)get函數(shù)先是調(diào)用do_GET函數(shù)給客戶端返回response頭部,并返回請(qǐng)求的文件,最后調(diào)用copyfile函數(shù)將請(qǐng)求文件通過連接返回給客戶端;

  以上就是http模塊最基礎(chǔ)的內(nèi)容,最后,總結(jié)下例子程序handler部分:

1. server把請(qǐng)求傳給SimpleHTTPRequestHandler初始化函數(shù);

2. SimpleHTTPRequestHandler在初始化部分,對(duì)這個(gè)客戶端connection進(jìn)行一些設(shè)置;

3. 接著調(diào)用handle函數(shù)處理請(qǐng)求;

4. handle函數(shù)接著調(diào)用handle_one_request處理請(qǐng)求;

5. handle_one_request函數(shù)內(nèi)部,解析請(qǐng)求,找到請(qǐng)求處理函數(shù);

6. 我之前的訪問屬于get訪問,因此直接調(diào)用do_GET函數(shù)將index.html文件返回給客戶端;

python http模塊到此已經(jīng)分析結(jié)束;不知道大家有沒發(fā)現(xiàn),python自帶的http模塊使用起來不是很方便,因?yàn)樗峭ㄟ^請(qǐng)求方法來調(diào)用請(qǐng)求函數(shù),這樣當(dāng)同一方法調(diào)用次數(shù)非常多時(shí),例如getpost方法,會(huì)導(dǎo)致這個(gè)請(qǐng)求函數(shù)異常龐大,代碼不好編寫,各種情況判斷;當(dāng)然SimpleHTTPRequestHandler只是python提供的一個(gè)簡(jiǎn)單例子而已;

  當(dāng)然,python官方提供了針對(duì)http更好用的框架,即wsgi serverwsgi application;接下來文章先分析python自帶的wsgiref模塊以及bottle,后面再分析flask;

 

文章來源:羅道文的私房菜

您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?