zoukankan      html  css  js  c++  java
  • SocketServerBaseHTTPServer,GET方法request到response的正常响应过程浅析

    简要的分析一下一个GET方法的正常请求,到生成一个正常的responce响应信息的过程。以python中标准模块BaseHTTPServer模块为例,以下是代码:

    from BaseHTTPServer import BaseHTTPRequestHandler
    import urlparse
    
    class GetHandler(BaseHTTPRequestHandler):
        
        def do_GET(self):
            parsed_path = urlparse.urlparse(self.path)
            message_parts = [
                    'CLIENT VALUES:',
                    'client_address=%s (%s)' % (self.client_address,
                                                self.address_string()),
                    'command=%s' % self.command,
                    'path=%s' % self.path,
                    'real path=%s' % parsed_path.path,
                    'query=%s' % parsed_path.query,
                    'request_version=%s' % self.request_version,
                    '',
                    'SERVER VALUES:',
                    'server_version=%s' % self.server_version,
                    'sys_version=%s' % self.sys_version,
                    'protocol_version=%s' % self.protocol_version,
                    '',
                    'HEADERS RECEIVED:',
                    ]
            for name, value in sorted(self.headers.items()):                            
                message_parts.append('%s=%s' % (name, value.rstrip()))
            message_parts.append('')
            message = '
    '.join(message_parts)
            self.send_response(200)
            self.end_headers()
            self.wfile.write(message)
            return
    
    if __name__ == '__main__':
        from BaseHTTPServer import HTTPServer
        server = HTTPServer(('localhost', 8080), GetHandler)
        print 'Starting server, use <Ctrl-C> to stop'
        server.serve_forever()
    

    程序运行后,首先建立一个HTTPServer服务器,将服务器地址和请求处理的类绑定到服务器,这里的初始化绑定方式代码如下:

    class BaseServer:
        timeout = None
        def __init__(self, server_address, RequestHandlerClass):       
            self.server_address = server_address
            self.RequestHandlerClass = RequestHandlerClass
            self.__is_shut_down = threading.Event()
            self.__shutdown_request = False
    class TCPServer(BaseServer):
         """省略部分代码"""    
         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)
    

    上述类的继承关系是:BaseServer(SocketServer模块)---->TCPServer(SocketServer模块)---->HTTPServer(BaseHTTPServer模块),实例化时,TCPServer调用父类的__init__方法,完成地址的和类的绑定。

    接着服务器对象调用serve.forever()方法,等待服务端请求。当服务端请求进来后,该方法通过调用线程threading模块处理请求, 此线程调用_handle_request_noblock方法,此方法调用get_request获得一个socket对象赋值给request和一个客户端地址client_address

    def serve_forever(self, poll_interval=0.5):
        try:
                while not self.__shutdown_request:              
                    r, w, e = _eintr_retry(select.select, [self], [], [],          
                                           poll_interval)
                    if self in r:
                        self._handle_request_noblock()
    
    def _handle_request_noblock(self):       
            try:
                request, client_address = self.get_request()                                          #get_request方法是TCPServer类的方法                  
            except socket.error:
                return
            if self.verify_request(request, client_address):                        
                try:
                    self.process_request(request, client_address)                   
                except:
                    self.handle_error(request, client_address)                                                    #对于错误的响应方法,我们不去讨论
                    self.shutdown_request(request) 
    
    def get_request(self):
      '''TCPServer类__init__方法创建了一个基于internet和TCp协议的socket对象'''                             
            return self.socket.accept()                        #获取请求的socket对象和地址,这是线程级的真正接收request的方法
    

    调用get_request返回值后,接着调用verify_request验证requestclient_address,源码中是return True,所以总是会通过。通过后,调用process_requestprocess_request方法调用finish_request,finish_request实例化RequestHandlerClass也就是初始化时传进来的

    def process_request(self, request, client_address):
            self.finish_request(request, client_address)                              #调用finish_request
            self.shutdown_request(request)         
    
    def finish_request(self, request, client_address):
            self.RequestHandlerClass(request, client_address, self)                                         
    
    将请求传给RequestHandler后,进入对请求的处理和响应阶段。

    RequestHandlerClass实例化:

    class BaseRequestHandler:
    
        def __init__(self, request, client_address, server):                                                                                                                 # request传给类对象之后,在这里初始化。
            self.request = request
            self.client_address = client_address
            self.server = server 
            self.setup()                                                 #调用setup
            try:
                self.handle()                                        #调用handle()
            finally:
                self.finish()                                                                                                                                                                                  #调用finish
    

    初始化首先调用setup方法:

    def setup(self):
            self.connection = self.request                                     
            if self.timeout is not None:
                self.connection.settimeout(self.timeout)
            if self.disable_nagle_algorithm:
                self.connection.setsockopt(socket.IPPROTO_TCP,
                                           socket.TCP_NODELAY, True)
      #调用makefile方法,返回一个与socket相对应的文件对象赋值给相应的属性, 后面的代码raw_requestline要用到.
            self.rfile = self.connection.makefile('rb', self.rbufsize)                                                                                                     
            self.wfile = self.connection.makefile('wb', self.wbufsize)                      
    

    setup调用完后,接着调用handle方法,此方法调用handle_one_request方法:

    '''属于BaseHTTPServer类'''
    def handle(self):       
            self.close_connection = 1
            self.handle_one_request()                                                                                    
            while not self.close_connection:
                self.handle_one_request() 
        
    def handle_one_request(self):
            try:
                self.raw_requestline = self.rfile.readline(65537)                                                                                                   #setup里面的rfile.readline
                if len(self.raw_requestline) > 65536:                                                                                                                         
                    self.requestline = ''
                    self.request_version = ''
                    self.command = ''
                    self.send_error(414)
                    return                                                                                                                                                                          #不正常的响应,不讨论
                if not self.raw_requestline:
                    self.close_connection = 1
                    return                                                                                                                                                                    
                if not self.parse_request():                                                                                                                                        #调用parse_request方法
                    # An error code has been sent, just exit
                    return
                mname = 'do_' + self.command                                                                                                                                
                if not hasattr(self, mname):
                    self.send_error(501, "Unsupported method (%r)" % self.command)
                    return
                method = getattr(self, mname)
                method()        							                                                                                   #根据返回的command值,调用method,这里是调用do_GET
                self.wfile.flush() #actually send the response if not already done.
            except socket.timeout, e:
                #a read or a write timed out.  Discard this connection
                self.log_error("Request timed out: %r", e)
                self.close_connection = 1
                return
    

    上述handle_one_request方法中,首先调用parse_request方法,根据该方法的返回值,调用合适的command。首先分析parse_request方法:

     def parse_request(self):                                                      
            self.command = None  # set in case of error on the first line
            self.request_version = version = self.default_request_version
            self.close_connection = 1
            requestline = self.raw_requestline    `parse_request`         					                        #raw_requestline从self.raw_requestline = self.rfile.readline(65537)获得
            requestline = requestline.rstrip('
    ')
            self.requestline = requestline
            words = requestline.split()                                                                                                                                  
            if len(words) == 3:
                command, path, version = words                                                                                                                 #command  path  version赋值,在GET中首页中大部分都是‘GET’,‘/’,‘HTTP/1.0' 
               '''省略部分代码'''
            self.command, self.path, self.request_version = command, path, version                                                  #path command request_version,赋值成类的属性
    
            # Examine the headers and look for a Connection `RequestHandlerClass`实例化directive
            self.headers = self.MessageClass(self.rfile, 0)                                                                                                  #self.headers返回的信息
            '''省略代码'''
            return True                                     #正常返回,即调用command
    

    parse_request正常返回后,此时调用command,这里就是在GetHandler类中定义的do_GET方法。

     def do_GET(self):
            parsed_path = urlparse.urlparse(self.path)                                                                                                      #解析path信息
            message_parts = [
                    'CLIENT VALUES:',
                    'client_address=%s (%s)' % (self.client_address,
                                                self.address_string()),
                    'command=%s' % self.command,
                    'path=%s' % self.path,
                    'real path=%s' % parsed_path.path,
                    'query=%s' % parsed_path.query,
                    'request_version=%s' % self.request_version,
                    '',
                    'SERVER VALUES:',
                    'server_version=%s' % self.server_version,
                    'sys_version=%s' % self.sys_version,
                    'protocol_version=%s' % self.protocol_version,
                    '',
                    'HEADERS RECEIVED:',
                    ]
            for name, value in sorted(self.headers.items()):                                            #迭代在parse_request中得到的头信息。
                message_parts.append('%s=%s' % (name, value.rstrip()))
            message_parts.append('')
            message = '
    '.join(message_parts)
            self.send_response(200)                                 #返回响应信息,包含请求中的信息和自己生成的响应头
            self.end_headers()
            self.wfile.write(message)
            return
    

    至此整个请求响应过程完毕。在RequestHandlerClass实例化时首先是调用self.handle(),最终生成响应返回,最后调用self.finish() 方法,结束当前线程。

    上面粗线条的勾勒了一下整个请求响应过程,整个过程大致是请求进来,经过BaseServer-->TCPServer-->HTTPServer-->BaseRequestHandler-->StreamRequestHandler-->BaseHTTPRequestHandler-->自定义APP,完成响应。

    主要参考:
    https://pymotw.com/2/BaseHTTPServer/index.html#module-BaseHTTPServer

  • 相关阅读:
    [剑指Offer] 59.按之字形顺序打印二叉树
    [剑指Offer] 58.对称的二叉树
    [剑指Offer] 57.二叉树的下一个结点
    [剑指Offer] 56.删除链表中重复的结点
    [剑指Offer] 55.链表中环的入口结点
    [计算机网络] C++模拟telnet登陆SMTP服务发送邮件过程
    [计算机网络-应用层] 因特网中的电子邮件
    [计算机网络-应用层] DNS:因特网的目录服务
    [剑指Offer] 54.字符流中的第一个不重复的字符
    [剑指Offer] 53.表示数值的字符串
  • 原文地址:https://www.cnblogs.com/hyperionworld/p/5353460.html
Copyright © 2011-2022 走看看