• Python小项目四:实现简单的web服务器


    https://blog.csdn.net/u010103202/article/details/74002538

    本博客是整理在学习实验楼的课程过程中记录下的笔记形成的,参考:https://www.shiyanlou.com/courses/552。不同之处在于实验楼使用python2.7,而博主这里使用的是python3.6。在学习中也因为python版本不同遇到了一些坑,这里写成博客一作记录,二来可以帮助像博主这样的入门者少踩一些坑。

    要想实现web服务器,首先要明白web服务器应该具备怎样的功能:比如说浏览器发送了http请求(GET或POST),服务器要首先接收客户端发送的TCP请求,与之建立链接,然后接收http请求,解析并响应。 之后就是客户端的事情了,客户端接受响应并解析,显示,之后服务器断开链接。

    为了能很好地理解上面这个过程,我分别查询了以下概念:

    1. HTTP协议

    对于 http://www.google.com 这个网址,我们叫url,而http则是服务于url的协议。另外,url和ip地址两者的一一对应是通过DNS(域名解析系统)来完成的。

    浏览器的页面中包含CSS,html,JavaScript,视频文件、图片文件等等。我的理解就是html协议规定了网页元素的表达,一个html文件可以视为用编程语言写出来的网页内容。而html本身也指这个规定本身。

    而互联网的概念是:所有设备都提供独特的标签(总称互联网协议地址或IP地址),有互联网服务供应商(ISP)提供的公网IP地址,通过这些地址,可以进行通信。

    如下图:

    2. web服务器的基本概念,包括链接建立后的传输过程

    这时候,我们对整个过程有了大致的了解,要对其进行实现我们需要做瑞星啊几件事:

    * 接受TCP请求可使用http.server库来自动完成(注意,python3使用这个库,但是实验楼里用python2.7用的是另一个库)。

    伪代码如下:

    1.  
      from http.server import HTTPServer, 某个handler类
    2.  
       
    3.  
      httpd = HTTPServer( url地址, handler类)


    其中HTTPServer作用是创建并监听HTTP socket,解析请求给handler类。url地址即服务器url,handler类在http.server中有三种,这里用BaseHTTPRequestHandler,该类本身不能响应任何实际的HTTP请求,因此需要定义子类来处理每个请求方法(GET/POST),实际上就是空的handler类,允许用户自定义处理方法。

    在本次实验中值处理GET请求——相应的在子类中定义(给出)do_GET()函数即可。

    上面内容中也提到了socket,为了更好地理解我也查询了相关内容。注意python中的大部分网络编程模块都隐藏了socket模块的细节,不直接和套接字交互。所以这里我们只需要理解即可,具体编程不需要考虑其中内容。

      socket套接字是做什么用的?-->两个端点的程序之间的“信息通道”。即计算机1中的程序与计算机2中的程序通过socket发送信息。套接字是一个类,一个套接字是socket模块中的socket类中的一个实例。一个套接字处理一次通讯(服务器和客户机),各自进行设置,对应有不同的方法,比如说,s.connect就是客户机,s.listen(5)就是服务器。

    连接方式在于一个connect(),一个listen(),使用accept()方法来完成。(accept()是服务器端开始监听后,可以接受客户端的连接。)accept返回(client,address)元祖,client是客户端套接字,而address是地址。处理完与该客户端的连接后,再次调用accept方法开始等待下一个连接。

    总结来说,在这个实验里,我们要实现的功能只是根据用户的请求,生成http响应。所以我们也应该知道http请求和响应的格式:

    --------------------------------------------------------------------------------------------------------------------------------------------------------------

    一. 实现静态页面

    接下来按照我实验时的步骤来分别记录。

    步骤1. 首先建立一个简单web服务器, 能够响应静态页面

    首先在主函数中,固定的使用以下语句即可:

    1.  
      if __name__ == '__main__':
    2.  
       
    3.  
      httpAddress = ('', 8030)
    4.  
       
    5.  
      httpd = hs.HTTPServer(httpAddress, RequestHandler)
    6.  
       
    7.  
      httpd.serve_forever()

    这里url地址空缺则代表本机地址127.0.0.1,端口可以改动(有些端口系统占用着)。

    所以为了让上述代码运行起来,我们的主要内容在于实现RequestHandler。

    之前提到过,使用BaseHTTPRequestHandler,则需要定义一个子类,并在子类中给出do_GET(),页面设计等内容。如下代码所示,我们在该类中给出依照http响应的格式写出的内容,再在do_GET()函数中将该内容作为响应返回。相当于我们在RequestHandler类中给出了http的响应。

    1.  
      class RequestHandler(hs.BaseHTTPRequestHandler):
    2.  
       
    3.  
      def send_content(self, page, status = 200):
    4.  
       
    5.  
      self.send_response(status)
    6.  
      self.send_header("Content-type", "text/html")
    7.  
      self.send_header("Content-Length", str(len(page)))
    8.  
      self.end_headers()
    9.  
      self.wfile.write(bytes(page, encoding = 'utf-8'))
    10.  
      #print(page)
    11.  
       
    12.  
      def do_GET(self):
    13.  
      获取路径,
    14.  
      执行操作(send_content)

    而我们同样要判断在什么样的情况下我们给出上述响应,同时处理不合理的请求和异常。

    因此接下来我们要写do_GET()的具体逻辑和代码,假设静态页面存在了plain.html中,那么合理的url是127.0.0.1:8030/plain.html,而其对于他的请求服务器是不能做出反应的。所以do_GET()的重点在于判断输入合理与否:我们将输入分为三种情况:路径不存在、路径不是一个文件、路径是一个文件。

    1.  
      def do_GET(self):
    2.  
      #这里要处理两个异常,一个是读入路径时可能出现的异常,一个是读入路径后若不是文件,要作为异常处理
    3.  
      try:
    4.  
       
    5.  
      #获取文件路径
    6.  
      full_path = os.getcwd() + self.path
    7.  
       
    8.  
      # 如果路径不存在
    9.  
      if not os.path.exists(full_path):
    10.  
       
    11.  
      raise ServerException("'{0}' not found".format(self.path))
    12.  
       
    13.  
      #如果该路径是一个文件
    14.  
      elif os.path.isfile(full_path):
    15.  
       
    16.  
      self.handle_file(full_path)
    17.  
       
    18.  
      #如果该路径不是一个文件
    19.  
      else:
    20.  
       
    21.  
      raise ServerException("Unknown object '{0}'".format(self.path))
    22.  
       
    23.  
      except Exception as msg:
    24.  
       
    25.  
      self.handle_error(msg)

    这里的异常是异常中基类Exception的子类,即

    1.  
      class ServerException(Exception):
    2.  
       
    3.  
      '''服务器内部错误'''
    4.  
       
    5.  
      pass

    里面什么都不干,但是利用Exception我们可以对异常报相应的错误信息。raise 语句中括号中就是异常的提示信息。

    /* 这里

    "Unknown object '{0}'".format(self.path)

    用到了字符串的format方法,format是格式化输出的方法,即最终显示的是format括号内的内容代替{0}中的内容后的字符串内容。当然format的用法还有更为复杂的形式,如后面会见到的“”.format(**字典),这个语句中有另外一个知识点,**dict。**dict作为函数的参数时,是用键值对应函数中的参数名,而用值作为函数的输入值。而在字符串.format中,用字典的键匹配字符串中{}里的内容,而用值去依次替换,如

    1.  
      d = {'x':1, 'y':2}
    2.  
      str = “Pages show {x} and {y}”
    3.  
      print(str.format(**d))
    4.  
      #将显示Pages show 1 and 2

    */

    有关文件路径:

    #获取文件路径 fullpath = os.getcwd() + self.path (+号前得到当前路径,后面是得到handler得到的路径,如/plain.html

    #判断路径是否存在 os.path.exist(fullpath)

    #判断路径是否是文件 os.path.isfile(fullpath)

    处理并显示内容 

    #从文件中得到内容 content = file.read() (注意content此处需要字符串,所以open('r')以r方式而非rb,rb读入是byte类型。

    /* 这里需要说明下,python3.6对于字符串还是byte有明确区分,所以读入时要用'r' 还是‘rb’要注意。之前有关python项目中也提到过这个问题。*/

    以上内容都了解后,我们就可以实现出一个响应静态页面的服务器,当然,你需要有plain.html文件放在和你python代码的相同目录下。你可以在https://drive.google.com/file/d/0By68FgZpORkFOWZKS1dzeHpfTlk/view?usp=sharing下载得到。

    下载httpserver_plain.py文件和plain.html文件即可测试以上介绍的内容。(csdn上传以后不能删除不能修改,这里必须疯狂吐槽)

    /* 如何测试?

    你可以用cmd打开终端,运行以上python代码(命令为python httpserver_plain.html),之后在浏览器中输入127.0.0.1:你设置的端口号/plain.html.查看效果。

    或者pip安装httpie,终端输入http 127.0.0.1:你设置的端口号/plain.html来查看调用效果。

    */

    示意代码如下:

    1.  
      # -*- coding: utf-8 -*-
    2.  
      """
    3.  
      Created on Fri Jun 23 08:13:43 2017
    4.  
       
    5.  
      @author: dc
    6.  
      """
    7.  
       
    8.  
      import http.server as hs
    9.  
      import sys, os
    10.  
       
    11.  
       
    12.  
      class ServerException(Exception):
    13.  
       
    14.  
      '''服务器内部错误'''
    15.  
       
    16.  
      pass
    17.  
       
    18.  
      class RequestHandler(hs.BaseHTTPRequestHandler):
    19.  
       
    20.  
      def send_content(self, page, status = 200):
    21.  
       
    22.  
      self.send_response(status)
    23.  
      self.send_header("Content-type", "text/html")
    24.  
      self.send_header("Content-Length", str(len(page)))
    25.  
      self.end_headers()
    26.  
      self.wfile.write(bytes(page, encoding = 'utf-8'))
    27.  
      #print(page)
    28.  
       
    29.  
      def do_GET(self):
    30.  
      #这里要处理两个异常,一个是读入路径时可能出现的异常,一个是读入路径后若不是文件,要作为异常处理
    31.  
      try:
    32.  
       
    33.  
      #获取文件路径
    34.  
      full_path = os.getcwd() + self.path
    35.  
       
    36.  
      # 如果路径不存在
    37.  
      if not os.path.exists(full_path):
    38.  
       
    39.  
      raise ServerException("'{0}' not found".format(self.path))
    40.  
       
    41.  
      #如果该路径是一个文件
    42.  
      elif os.path.isfile(full_path):
    43.  
       
    44.  
      self.handle_file(full_path)
    45.  
       
    46.  
      #如果该路径不是一个文件
    47.  
      else:
    48.  
       
    49.  
      raise ServerException("Unknown object '{0}'".format(self.path))
    50.  
       
    51.  
      except Exception as msg:
    52.  
       
    53.  
      self.handle_error(msg)
    54.  
       
    55.  
       
    56.  
      def handle_file(self, full_path):
    57.  
       
    58.  
      try:
    59.  
       
    60.  
      with open(full_path, 'r') as file:
    61.  
       
    62.  
      content = file.read()
    63.  
       
    64.  
       
    65.  
      self.send_content(content,200)
    66.  
       
    67.  
      except IOError as msg:
    68.  
       
    69.  
      msg = "'{0}' cannot be read: {1}".format(self.path, msg)
    70.  
       
    71.  
      self.handle_error(msg)
    72.  
       
    73.  
      Error_Page = """
    74.  
      <html>
    75.  
      <body>
    76.  
      <h1>Error accessing {path}</h1>
    77.  
      <p>{msg}</p>
    78.  
      </body>
    79.  
      </html>
    80.  
      """
    81.  
       
    82.  
      def handle_error(self, msg):
    83.  
       
    84.  
      content = self.Error_Page.format(path= self.path,msg= msg)
    85.  
       
    86.  
      self.send_content(content, 404)
    87.  
       
    88.  
       
    89.  
       
    90.  
       
    91.  
       
    92.  
      if __name__ == '__main__':
    93.  
       
    94.  
      httpAddress = ('', 8030)
    95.  
       
    96.  
      httpd = hs.HTTPServer(httpAddress, RequestHandler)
    97.  
       
    98.  
      httpd.serve_forever()



    -------------------------------------------------------------------------------------------------------------------

    二. 当可以响应静态页面之后,我们接着实现CGI协议与脚本。

    某些请求可以用另外编写脚本来处理(给出响应),这样对于新增的一些请求,就不用每次都修改服务器脚本了。为了更好地理解CGI,我们需要知道以下基本概念。

    与之前实现静态页面相对比,这里实现cgi脚本有何不同?

    --> 我们在访问静态页面时,输入127.0.0.1:8030/plain.html,服务器会为我们返回plain.html文件的内容;而cgi脚本我们访问的是一个脚本,即127.0.0.1:8030/time.py,返回的是执行外部命令并获得的输出。

    1. 第一个内容就是如何实现执行外部命令获得该输出

    我们使用subprocess库,具体代码是:

    1.  
      subprocess.check_output(['cmd', 'arg1', 'arg2'])
    2.  
      本例中为data = subprocess.check_output(['python', fullpath])

    2. 第二个内容是要在if语句中判断是否路径中文件是否以指定后缀结尾

    可用字符串方法endswith()判断是否以".py"结尾。(该方法以".py"作为参数,输出bool值)

    当实现了以上两个功能后,我们只需在类似静态页面的实现那样填补代码逻辑即可,示意代码如下:

    1.  
      # -*- coding: utf-8 -*-
    2.  
      """
    3.  
      Created on Sun Jun 25 03:38:11 2017
    4.  
       
    5.  
      @author: dc
    6.  
      """
    7.  
       
    8.  
       
    9.  
      import http.server as hs
    10.  
      import sys, os
    11.  
      import subprocess
    12.  
       
    13.  
      class ServerException(Exception):
    14.  
       
    15.  
      '''服务器内部错误'''
    16.  
       
    17.  
      pass
    18.  
       
    19.  
      # 如果路径不存在
    20.  
      class case_no_path(object):
    21.  
      '''如果路径不存在'''
    22.  
       
    23.  
      def test(self, handler):
    24.  
       
    25.  
      return not os.path.exists(handler.full_path)
    26.  
       
    27.  
      def act(self, handler):
    28.  
       
    29.  
      raise ServerException("{0} not found".format(handler.path))
    30.  
       
    31.  
      #所有情况都不符合时的默认处理类
    32.  
      class case_allother_fail(object):
    33.  
      '''所有情况都不符合时的默认处理类'''
    34.  
       
    35.  
      def test(self, handler):
    36.  
       
    37.  
      return True
    38.  
       
    39.  
      def act(self, handler):
    40.  
       
    41.  
      raise ServerException("Unknown object {0}".format(handler.full_path))
    42.  
       
    43.  
      class case_is_file(object):
    44.  
      ''' 输入的路径是一个文件'''
    45.  
       
    46.  
      def test(self, handler):
    47.  
       
    48.  
      return os.path.isfile(handler.full_path)
    49.  
       
    50.  
      def act(self, handler):
    51.  
       
    52.  
      handler.handle_file(handler.full_path)
    53.  
       
    54.  
       
    55.  
      class case_CGI_file(object):
    56.  
       
    57.  
      def test(self, handler):
    58.  
       
    59.  
      print(os.path.isfile(handler.full_path) and handler.full_path.endswith('.py'))
    60.  
      return os.path.isfile(handler.full_path) and
    61.  
      handler.full_path.endswith('.py')
    62.  
       
    63.  
      def act(self, handler):
    64.  
       
    65.  
      handler.run_cgi(handler.full_path)
    66.  
       
    67.  
      class case_index_file(object):
    68.  
      '''输入跟url时显示index.html'''
    69.  
       
    70.  
      def index_path(self, handler):
    71.  
       
    72.  
      return os.path.join(handler.full_path, 'index.html')
    73.  
       
    74.  
       
    75.  
      #判断目标路径是否是目录,且需要判断目录下是否包含index.html
    76.  
      def test(self, handler):
    77.  
       
    78.  
      return os.path.isdir(handler.full_path) and
    79.  
      os.path.isfile(self.index_path(handler))
    80.  
       
    81.  
      def act(self, handler):
    82.  
       
    83.  
      handler.handle_file(self.index_path(handler))
    84.  
       
    85.  
       
    86.  
      class RequestHandler(hs.BaseHTTPRequestHandler):
    87.  
      '''
    88.  
      请求路径合法则返回相应处理,
    89.  
      否则返回错误页面
    90.  
      '''
    91.  
       
    92.  
      full_path = ""
    93.  
       
    94.  
      #一定要注意条件类的优先顺序不同,对于文件的捕捉能力也不同,越是针对某种特例的条件类,
    95.  
      #越应该放在前面。
    96.  
      cases = [case_no_path(),
    97.  
      case_CGI_file(),
    98.  
      case_is_file(),
    99.  
      case_index_file(),
    100.  
      case_allother_fail()]
    101.  
       
    102.  
       
    103.  
       
    104.  
      def run_cgi(self, fullpath):
    105.  
       
    106.  
      #运行cgi脚本并得到格式化的输出,从而可以显示到浏览器上
    107.  
      data = subprocess.check_output(["python", fullpath])
    108.  
       
    109.  
      #self.wfile.write(bytes(fullpath, encoding = 'utf-8'))
    110.  
       
    111.  
      self.send_content(page = str(data, encoding = 'utf-8'))
    112.  
       
    113.  
       
    114.  
      def send_content(self, page, status = 200):
    115.  
       
    116.  
      self.send_response(status)
    117.  
      self.send_header("Content-type", "text/html")
    118.  
      self.send_header("Content-Length", str(len(page)))
    119.  
      self.end_headers()
    120.  
      self.wfile.write(bytes(page, encoding = 'utf-8'))
    121.  
      #print(page)
    122.  
       
    123.  
       
    124.  
      def do_GET(self):
    125.  
      #这里要处理两个异常,一个是读入路径时可能出现的异常,一个是读入路径后若不是文件,要作为异常处理
    126.  
      try:
    127.  
       
    128.  
      #获取文件路径
    129.  
      self.full_path = os.getcwd() + self.path
    130.  
       
    131.  
       
    132.  
       
    133.  
      # 如果路径不存在
    134.  
      for case in self.cases:
    135.  
       
    136.  
      if case.test(self):
    137.  
       
    138.  
      case.act(self)
    139.  
       
    140.  
      break
    141.  
       
    142.  
      except Exception as msg:
    143.  
       
    144.  
      self.handle_error(msg)
    145.  
       
    146.  
       
    147.  
      def handle_file(self, full_path):
    148.  
       
    149.  
      try:
    150.  
       
    151.  
      with open(full_path, 'r') as file:
    152.  
       
    153.  
      content = file.read()
    154.  
       
    155.  
       
    156.  
      self.send_content(content,200)
    157.  
       
    158.  
      except IOError as msg:
    159.  
       
    160.  
      msg = "'{0}' cannot be read: {1}".format(self.path, msg)
    161.  
       
    162.  
      self.handle_error(msg)
    163.  
       
    164.  
      Error_Page = """
    165.  
      <html>
    166.  
      <body>
    167.  
      <h1>Error accessing {path}</h1>
    168.  
      <p>{msg}</p>
    169.  
      </body>
    170.  
      </html>
    171.  
      """
    172.  
       
    173.  
      def handle_error(self, msg):
    174.  
       
    175.  
      content = self.Error_Page.format(path= self.path,msg= msg)
    176.  
       
    177.  
      self.send_content(content, 404)
    178.  
       
    179.  
       
    180.  
       
    181.  
       
    182.  
       
    183.  
      if __name__ == '__main__':
    184.  
       
    185.  
      httpAddress = ('', 8090)
    186.  
       
    187.  
      httpd = hs.HTTPServer(httpAddress, RequestHandler)
    188.  
       
    189.  
      httpd.serve_forever()


    注意到主要的区别在于我们在RequestHandler类中实现了run_cgi(self, fullpath),用来从外部执行请求的脚本内容(如这里的time.py);而条件中我们也加入了后缀的判断。

    整个代码运行效果是,当我们在终端输入http 127.0.0.1:端口号/time.py,则服务器会执行time.py的结果作为响应返回。该代码同样包含在上述下载链接内,包含httpserver_CGI.py和time.py。

    -----------------------------------------------------------------------------------------------------------------------------------------

    三. 代码整理和重构

    3.1 条件类

    从上述plain和cgi的两个示意代码中,大家可能已经发现:在对不同条件的判断中,两个代码分别使用了if-elif-else语句形式和条件类的形式。其中前者理解很容易,而后者条件类是指将条件放置在不同的类中,然后循环遍历这些类,看哪个符合则对应执行相应条件。这样处理的好处在于易于维护:对于新加入的条件,不对改动if-elif-else使其变得臃肿,而只需增加一个类作为条件,同时在handler中循环遍历即可。

    如我们要增加一个功能:在输入127.0.0.1:端口号时,我们希望得到主页的显示(存为index.html),这时我们就新建一个条件类:

    1.  
      class case_index_file(object):
    2.  
      '''输入跟url时显示index.html'''
    3.  
       
    4.  
      def index_path(self, handler):
    5.  
       
    6.  
      return os.path.join(handler.full_path, 'index.html')
    7.  
       
    8.  
       
    9.  
      #判断目标路径是否是目录,且需要判断目录下是否包含index.html
    10.  
      def test(self, handler):
    11.  
       
    12.  
      return os.path.isdir(handler.full_path) and
    13.  
      os.path.isfile(self.index_path(handler))
    14.  
       
    15.  
      def act(self, handler):
    16.  
       
    17.  
      handler.handle_file(self.index_path(handler))

    同时在RequestHandler的实现中将其加入:

    1.  
      class RequestHandler(hs.BaseHTTPRequestHandler):
    2.  
      '''
    3.  
      请求路径合法则返回相应处理,
    4.  
      否则返回错误页面
    5.  
      '''
    6.  
       
    7.  
      full_path = ""
    8.  
       
    9.  
      cases = [case_no_path(),
    10.  
      case_index_file(),
    11.  
      case_is_file(),
    12.  
      case_allother_fail()]
    1.  
         def do_GET(self):
    2.  
         #这里要处理两个异常,一个是读入路径时可能出现的异常,一个是读入路径后若不是文件,要作为异常处理    
    3.  
              try:
    4.  
                  
    5.  
                  #获取文件路径
    6.  
                  self.full_path = os.getcwd() + self.path
    7.  
                  
    8.  
                                       
    9.  
                  
    10.  
                  # 如果路径不存在
    11.  
                  for case in self.cases:
    12.  
                      
    13.  
                      if case.test(self):
    14.  
                          
    15.  
                          case.act(self)
    16.  
                          
    17.  
                          break
    18.  
                  
    19.  
              except Exception as msg:
    20.  
              
    21.  
                  self.handle_error(msg)

    这样,我们就可以很方便的把新的条件加入进去,同时管理维护起来也很方便。该代码的实现也在上述链接中可以下载,包含两个文件:httpserver_index.py和index.html。

    3.2 代码重构

    这里的重构主要针对每个条件类中重复过的代码,我们可以通过构建基类,然后生成条件类时作为基类的子类生成即可,从而更好地维护代码。具体实现如下:

    1.  
      class base_case(object):
    2.  
      '''定义基类,用来处理不同的条件类,条件类继承该基类'''
    3.  
       
    4.  
      def handle_file(self, handler, full_path):
    5.  
       
    6.  
      try:
    7.  
       
    8.  
      with open(full_path, 'r') as file:
    9.  
       
    10.  
      content = file.read()
    11.  
       
    12.  
      handler.send_content(content,200)
    13.  
       
    14.  
      except IOError as msg:
    15.  
       
    16.  
      msg = "'{0}' cannot be read: {1}".format(self.path, msg)
    17.  
       
    18.  
      handler.handle_error(msg)
    19.  
       
    20.  
      def test(self, handler):
    21.  
       
    22.  
      assert False, "Not implemented."
    23.  
       
    24.  
      def act(self, handler):
    25.  
       
    26.  
      assert False, "Not implemented."

    里面对test和act的定义是通过断言来实现的,内在逻辑是:如果你子类不实现这两个方法, 那么你生成的子类是一定会出错的。于是这相当于是限定子类必须实现这两种方法。之后子类继承该基类即可:

    1.  
      # 如果路径不存在
    2.  
      class case_no_path(base_case):
    3.  
      '''如果路径不存在'''
    4.  
       
    5.  
      def test(self, handler):
    6.  
       
    7.  
      return not os.path.exists(handler.full_path)
    8.  
       
    9.  
      def act(self, handler):
    10.  
       
    11.  
      raise ServerException("{0} not found".format(handler.path))

    但是handle_file就不需要在RequestHandler中实现了,因为基类中已经包含了。该代码在连接中名为httpserver_baseclass.py。

    ----------------------------------------------------------------------------------------------------------

    以上就是我做实验楼实验的整个笔记,从一开始BaseHTTPServer模块import出错,找python3中的对应模块,到学习新模块,依次完成实验内容,中间有一些py2、3的不同的小坑,但是学完之后还是有不少收获。

    这里我在将学到的内容总结一下:

    1.http协议

    对于 http://www.google.com 这个网址,我们叫url,而http则是服务于url的协议。另外,url和ip地址两者的一一对应是通过DNS(域名解析系统)来完成的。

    浏览器的页面中包含CSS,html,JavaScript,视频文件、图片文件等等。我的理解就是html协议规定了网页元素的表达,一个html文件可以视为用编程语言写出来的网页内容。而html本身也指这个规定本身。

    而互联网的概念是:所有设备都提供独特的标签(总称互联网协议地址或IP地址),有互联网服务供应商(ISP)提供的公网IP地址,通过这些地址,可以进行通信。

    如下图:

    2. web服务器的基本概念,包括链接建立后的传输过程

    3. http请求格式


    4.http响应格式

    5. httpie库

    可以使用httpie库代替浏览器发送请求

    安装命令是 pip install httpie,

    使用命令是:http 网址(url)

    6. http.server库

    整个web服务器实现都是在使用这个库,他替我们解决tcp链接,请求解析等很多内容,我们只需要实现RequestHandler类的处理逻辑编写(这里也只涉及到了do_GET).

    7.socket模块

      socket套接字是做什么用的?-->两个端点的程序之间的“信息通道”。即计算机1中的程序与计算机2中的程序通过socket发送信息。套接字是一个类,一个套接字是socket模块中的socket类中的一个实例。一个套接字处理一次通讯(服务器和客户机),各自进行设置,对应有不同的方法,比如说,s.connect就是客户机,s.listen(5)就是服务器。

    连接方式在于一个connect(),一个listen(),使用accept()方法来完成。(accept()是服务器端开始监听后,可以接受客户端的连接。)accept返回(client,address)元祖,client是客户端套接字,而address是地址。处理完与该客户端的连接后,再次调用accept方法开始等待下一个连接。

    8.CGI

    (1)字符串format方法

    (2)**dict

    (3)str/byte转换

    1. os库

    #获取文件路径 fullpath = os.getcwd() + self.path (+号前得到当前路径,后面是得到handler得到的路径,如/plain.html

    #判断路径是否存在 os.path.exist(fullpath)

    #判断路径是否是文件 os.path.isfile(fullpath)

    2. subprocess库

    如何实现执行外部命令获得该输出

    我们使用subprocess库,具体代码是:

    1.  
      subprocess.check_output(['cmd', 'arg1', 'arg2'])
    2.  
      本例中为data = subprocess.check_output(['python', fullpath])

    3. 写基类,不鸡肋




    参考:

    http://www.cnblogs.com/biyeymyhjob/archive/2012/07/28/2612910.html

    http://www.magicsite.cn/blog/web/other/other298023.html

  • 相关阅读:
    第一张图:每天初始化交易流程图
    Django部署服务时候报错:SQLite 3.8.3 or later is required (found 3.7.17)
    LOJ2494. 「AHOI / HNOI2018」寻宝游戏
    luoguP4429 [BJOI2018]染色
    LOJ2720. 「NOI2018」你的名字
    LOJ2262. 「CTSC2017」网络
    LOJ2398. 「JOISC 2017 Day 3」自然公园
    ULR1 B. 【ULR #1】光伏元件
    CF Good Bye 2020 题解&总结 A~G
    vue 导入.md文件(markdown转HTML)
  • 原文地址:https://www.cnblogs.com/jukan/p/9373741.html
Copyright © 2020-2023  润新知