>  기사  >  백엔드 개발  >  Python과 함께 제공되는 http 모듈에 대한 자세한 설명

Python과 함께 제공되는 http 모듈에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-02-22 10:45:143970검색

블로거가 올해 또 다른 멋진 인턴십 경험을 시작하여 프로젝트를 배우고 작업하면서 시간이 이미 꽉 찼기 때문에 오랫동안 블로그를 하지 않았습니다. 이번 두 가지 경험에 매우 감사드립니다. golang과 Python을 접할 수 있게 해준 한 해, 다양한 언어를 배우고, 기존 c/C++ 학습의 한계를 뛰어넘고, golang과 Python의 뛰어난 기능을 배우고, 다양한 언어의 응용을 이해할 수 있었습니다. 이전에 Linux와 c/C++를 배운 덕분에 golang과 Python을 시작해 보겠습니다.

사용 방법을 배우는 것 외에도 내 학습 습관은 저도 소스코드를 공부하고 동작 메커니즘을 배워서 쉽게 사용할 수 있게, 즉 이런 언어들을 사용하는 걸 좋아합니다. 프레임워크가 일상생활에서 먹고 자는 것과 같아서 아주 자연스럽습니다. ; 최근에 접한 병앤플라스크 웹 프레임워크 때문에 이 둘의 소스코드를 살펴보고 싶은데, 이 두 프레임워크는 파이썬에 기본으로 제공되는 http를 기반으로 하고 있어서 이 글이 있습니다;

Python http의 간단한 예

Python http 프레임워크는 주로 서버와 핸들러로 구성됩니다. 서버는 주로 epoll을 사용하는 등 네트워크 모델을 구축하는 데 사용됩니다. 핸들러는 각 준비된 소켓을 처리하는 데 사용됩니다. 먼저 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.html 페이지.

이 예제의 서버 클래스는 HTTPServer를 사용하고 핸들러 클래스는 SimpleHTTPRequestHandler입니다. 따라서 HTTPServer는 요청 도착을 모니터링할 때 처리를 위해 SimpleHTTPRequestHandler 클래스에 요청을 보냅니다. 서버와 핸들러를 각각 분석합니다.

http 서버

http 모듈의 디자인은 tfs 파일을 읽었기 때문에 객체 지향 상속 다형성을 최대한 활용합니다. 이전에는 시스템 코드이므로 Python http를 볼 때 먼저 서버의 상속 관계를 제공하십시오.

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

상속 관계는 위 그림과 같습니다. 그 중 BaseServer와 TCPServer는 소켓서버.py 파일에 있고, HTTPServer는 http/server.py에 있습니다.

BaseServer

BaseServer는 모든 서버 클래스의 기반이기 때문에 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()

코드의 선택기는 실제로 IO 다중화입니다. select, poll, epoll 등을 캡슐화한 다음 서비스 자체에서 모니터링하는 소켓을 io 멀티플렉싱에 등록하고 이벤트 모니터링을 켭니다. 클라이언트가 연결되면 self._handle_request_noblock()이 호출되어 요청을 처리합니다. 이 처리 기능이 무엇인지 확인하세요.

_handle_request_noblock 함수는 먼저 클라이언트 연결 요청을 수신합니다. 실제로 시스템 호출 수락을 캡슐화합니다. 함수를 확인한 후 마지막으로 process_request를 호출하여 요청을 처리합니다. 여기서 get_request는 하위 클래스에 속하는 메소드입니다. 왜냐하면 tcp는 udp로 클라이언트 요청을 수신하는 것과 다르기 때문입니다(tcp에는 연결이 있고 udp에는 연결이 없습니다)

process_request가 구체적으로 어떤 일을 하는지 살펴보겠습니다.

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)

process_request 함수는 먼저 Finish_request를 호출하여 연결 처리를 완료합니다. shutdown_request 함수가 호출되어 연결이 종료되고, Finish_request 함수가 내부적으로 핸들러 클래스를 인스턴스화하고 클라이언트의 소켓과 주소를 전달합니다. 초기화가 완료되면 핸들러 클래스가 요청 처리를 완료한다고 설명합니다. 나중에 핸들러를 분석할 때

위는 BaseServer가 하는 일입니다. 이 BaseServer는 아직 일부 기능이 구현되지 않았으며 tcp/udp에 대한 추상화 계층 역할만 수행합니다. 요약:

이벤트 모니터링을 활성화하려면 먼저 Serve_forever를 호출하세요.

그런 다음 클라이언트 요청이 도착하면 요청이 처리를 위해 핸들러로 전달됩니다.


위에서 언급한 BaseServer에 의해 추상화된 기능을 보면 TCPServer 또는 UDPServer가 완료해야 하는 기능은 청취 소켓을 초기화하고 청취를 바인딩하고 마지막으로 클라이언트 요청이 있을 때 수행된다는 것을 알 수 있습니다. , 클라이언트를 수신합니다.

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)

TCPServer가 초기화되면 먼저 기본 클래스인 BaseServer의 초기화 함수를 호출합니다. 서버 주소, 핸들러 클래스 등을 사용한 후 자체 청취 소켓을 초기화하고 마지막으로 server_bind를 호출하여 소켓을 바인딩하고, server_activate 청취 소켓

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도 구현합니다. 클라이언트 요청을 수신하는 또 다른 기능인

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)

이전에 Linux 프로그래밍을 배운 적이 있다면 이 코드를 보는 것이 매우 익숙할 것입니다. 함수 이름은 Linux에서 제공하는 시스템 호출 이름과 정확히 동일합니다. 여기서는 자세히 설명하지 않겠습니다.

TCPServer는 실제로 tcp를 기반으로 서버의 기본 프레임워크를 구축했으므로 HTTPServer는 단지 TCPServer를 상속합니다. 그냥 server_bind 함수를 다시 로드하고, 재사용_주소 등을 설정하세요.

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

之前在看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 = 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

这个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 서버 및 wsgi 애플리케이션을 제공합니다. 다음 기사에서는 먼저 제공되는 wsgiref 모듈과 병을 분석합니다.

더 많은 Python이 함께 제공됩니다. http 모듈에 대한 관련 기사는 PHP 중국어 웹사이트에 주목하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.