Explication détaillée du module http fourni avec python

高洛峰
Libérer: 2017-02-22 10:45:14
original
3969 Les gens l'ont consulté

Je n'ai pas blogué depuis longtemps, car la blogueuse a commencé cette année une autre merveilleuse expérience de stage, en apprenant et en travaillant sur des projets, et le temps est déjà plein, je suis très reconnaissante pour ces deux expériences ; année pour m'avoir permis d'entrer en contact avec Golang et Python, d'apprendre différents langages, de sortir des limites de l'apprentissage C/C précédent, d'apprendre les excellentes fonctionnalités de Golang et Python et de comprendre l'application de différents langages​​ dans différents scénarios ; et l'apprentissage préalable de Linux et C/C m'a aussi fait rapidement Commençons avec golang et python ;

Mon habitude d'apprentissage, en plus d'apprendre à l'utiliser , j'aime aussi étudier le code source et apprendre le mécanisme de fonctionnement, pour pouvoir l'utiliser facilement ou en d'autres termes, utiliser ces langages​​ou Le framework, c'est comme manger et dormir dans la vie quotidienne, c'est très naturel ; parce que je suis récemment entré en contact avec les frameworks Web Bottle et Flask, je souhaite jeter un œil aux codes sources de ces deux-là, mais ces deux frameworks sont basés sur le http fourni avec Python, il y a donc cet article. ;

Un exemple simple de python http

Le framework python http est principalement composé d'un serveur et d'un gestionnaire. Le serveur est principalement utilisé pour créer des modèles de réseau, tels que l'utilisation. epoll pour surveiller les sockets. Le gestionnaire est utilisé pour traiter chaque socket prêt ; examinons d'abord l'utilisation simple de 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)
Copier après la connexion

Exécuter ce qui précède. exemple, vous pouvez obtenir ce qui suit :

python3 myhttp.py 127.0.0.1 9999
Copier après la connexion

À ce stade, si vous créez un nouveau fichier index.html dans le dossier actuel, vous pouvez passer http://127.0.0.1:9999/index.html La page index.html a été visitée.

La classe de serveur dans cet exemple utilise HTTPServer et la classe de gestionnaire est SimpleHTTPRequestHandler. Par conséquent, lorsque HTTPServer surveille l'arrivée d'une requête, il renvoie la requête à la classe SimpleHTTPRequestHandler pour traitement, ok, après avoir compris cela, nous démarrons respectivement le serveur et le gestionnaire d'analyse.

serveur http

La conception du module http utilise pleinement le polymorphisme d'héritage orienté objet, car j'ai lu des fichiers tfs avant. Code système, donc quand vous regardez python http, il n'y a pas tellement de pression ; donnez d'abord la relation d'héritage du serveur

 +------------------+
+------------+| tcpserver基类 |
| BaseServer +-------->| 开启事件循环监听 |
+-----+------+ | 处理客户端请求 |
 | +------------------+
 v +-----------------+
+------------+| httpserver基类 |
| TCPServer +-------->+设置监听socket |
+-----+------+ | 开启监听 |
 | +-----------------+
 v
+------------+
| HTTPServer | 
+------------+
Copier après la connexion

Le la relation d'héritage est comme indiqué dans l'image ci-dessus Display, où BaseServer et TCPServer se trouvent dans le fichier socketserver.py, HTTPServer est dans http/server.py ; regardons d'abord BaseServer ; >

Parce que BaseServer est tous les serveurs La classe de base, donc BaseServer résume autant que possible les points communs de tous les serveurs, comme l'activation de la boucle d'écoute des événements. C'est le point commun de chaque serveur, donc c'est aussi le cas. ce que fait principalement BaseServer ; jetons un coup d'œil à la partie principale du code de BaseServer

Le sélecteur dans le code encapsule en fait le multiplexage io de select, poll, epoll, etc., puis enregistre le socket surveillé par le service lui-même dans le multiplexage io. Réutilisez, activez la surveillance des événements, lorsqu'un client se connecte, self._handle_request_noblock() sera appelé pour traiter la requête ; à quoi fait cette fonction de traitement ;
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()
Copier après la connexion

La fonction _handle_request_noblock est une fonction interne. Elle reçoit d'abord la demande de connexion du client. La couche inférieure encapsule en fait le système. appelez la fonction accept, puis vérifie la demande et appelle enfin process_request pour traiter la demande ; où get_request appartient à la méthode de sous-classe, car tcp et udp reçoivent les demandes des clients différemment (tcp a une connexion, udp n'a pas de connexion)
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)
Copier après la connexion

Jetons un coup d'œil à ce que fait spécifiquement process_request ;

La fonction process_request appelle d'abord finish_request pour traiter une connexion une fois le traitement terminé. , la fonction shutdown_request est appelée pour fermer la connexion ; et la fonction finish_request instancie en interne une classe de gestionnaire et met le socket et l'adresse du client sont transmis, ce qui signifie que la classe de gestionnaire a terminé le traitement de la demande lorsque l'initialisation est terminée. regardez cela de plus près lorsque nous analyserons le gestionnaire plus tard ;
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)
Copier après la connexion

Ce qui précède est ce que fait BaseServer. Ce BaseServer ne peut pas être utilisé directement car certaines fonctions n'ont pas encore été implémentées et ne sont utilisées que comme couche d'abstraction pour TCP. /udp. Pour résumer :

Appelez d'abord serve_forever pour activer la surveillance des événements

Ensuite, lorsqu'une demande client arrive, la demande est transmise au gestionnaire pour traitement ;

TCPServer


Les fonctions résumées par le BaseServer mentionné ci-dessus, nous pouvons savoir que les fonctions que TCPServer ou UDPServer doivent remplir sont l'initialisation de l'écoute du Socket, et lier l'écouteur, et enfin recevoir le client lorsqu'il y a une demande client ; jetons un coup d'œil au code

Initialisation de TCPServer Tout d'abord, appelez la fonction d'initialisation de la classe de base BaseServer pour initialiser l'adresse du serveur, la classe du gestionnaire, etc., puis initialisez sa propre socket d'écoute, et enfin appelez server_bind pour lier la socket, server_activate socket d'écoute

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
Copier après la connexion

TCPServer implémente également une autre fonction, qui consiste à recevoir les requêtes des clients,

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)
Copier après la connexion

si avant Si vous avez étudié la programmation Linux, vous devriez vous sentir très familier en regardant à ces codes, car les noms de fonctions sont exactement les mêmes que les noms d'appels système fournis par Linux, donc je n'entrerai pas dans les détails ici

TCPServer a en fait intégré le framework principal du serveur basé sur TCP ; Il est configuré, donc HTTPServer ne surcharge que la fonction server_bind, définit reuse_address, etc. sur la base de l'héritage de TCPServer

;

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

httpd = ServerClass(server_address,HandlerClass)这行代码在初始化HTTPServer时,主要是调用基类TCPServer的初始化方法,初始化了监听的套接字,并绑定和监听;
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
Copier après la connexion

之前在看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()
Copier après la connexion

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()
Copier après la connexion

这就是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 = True
return
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 = True
return
Copier après la connexion

这个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()
Copier après la connexion

这个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文件返回给客户端;

L'analyse du module http python est terminée à ce stade ; je ne sais pas si vous avez remarqué que le module http fourni avec python n'est pas très pratique à utiliser, car il appelle le fonction de requête via la méthode de requête, donc lorsque la même méthode est appelée plusieurs fois, comme les méthodes get et post, la fonction de requête sera extrêmement volumineuse, ce qui rendra le code difficile à écrire et difficile à juger dans diverses situations bien sûr ; , SimpleHTTPRequestHandler n'est qu'un exemple simple fourni par python ;

Bien sûr, Python fournit officiellement un framework plus utile pour http, à savoir le serveur wsgi et l'application wsgi ; l'article suivant analysera d'abord le module wsgiref et la bouteille fournis ; avec python, puis analysez flask ;

Plus de python est fourni avec Veuillez faire attention au site Web PHP chinois pour les articles connexes sur le module http !

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!