用python撸出一个静态web服务器

python搭建简单的静态web服务器

储备知识
  • 一丢丢的python(io和多线程的知识)
  • 一丢丢的http协议
  • 一丢丢的tcp/ip协议(当然不了解也没关系)
  • 一丢丢的正则表达式知识
web服务器基本原理
  • 当在浏览器的地址栏输入一个ip与端口之后,浏览器就会通过tcp/ip协议与相应的主机端口进程建立联系。经历过三次握手之后就会将http请求发送到相应的服务器进程去,之前我们了解的http协议在服务进程收到的其实就是一串有特殊格式的字符串。

    当我们浏览器输入localhost:9876 后服务进程实际收到的如下:

    这里写图片描述

大致流程
  1. 在服务端建立tcp服务进程,为了保证服务端可以同时处理多个请求,我们需要在每接受一个请求后为其单独使用一个线程(或者进程)为其进行服务。

    server = socket(AF_INET, SOCK_STREAM)
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    address = ('', 9876)
    server.bind(address)
    server.listen(10)
    try:
       while True:
           print("-------等待接受服务----------")
           client, client_address = server.accept()
           print("-------接受服务成功----------")
           # 如果使用进程服务,可以在在后面把client关闭。
           p = Thread(target=deal_socket, args=(client,))
           p.start()
    except Exception as e:
          print(e)
    finally:
          server.close()
          print("-------服务结束----------")

    这里的deal_socket函数就是我们为一个请求服务的函数,在一个单独的线程中运行。

  2. 在处理url请求的函数中,我们需要读取出客户端的http请求。

    def deal_socket(client):
       print("-------开启新的线程----------")
       try:
           data = client.recv(1024)
           if len(data) > 0:
               fileName = get_request_name_from_http(data.decode("utf-8"))
               writeHtml(client, fileName)
    
       finally:
           client.close()
           print("-------关闭新的线程----------")
    • 在这里的data就是我们读取到的http服务请求,当其长度等于0时代表客户端已经关闭了tcp连接。
    • 这里的get_request_name_from_http()需要我们解析出请求的静态资源
    • 这里的writeHtml()将静态文本写回到客户端。
  3. get_request_name_from_http函数中我们需要从原始的url请求中解析出http请求中我们需要的请求资源部分,这里我们可以通过正则表达式完成简单的完成解析。

    def get_request_name_from_http(http):
       # 注意这里通过非贪婪模式匹配
       r = re.search(r"GET /(.+?) ", http)
       fileName = ""
       if r != None:
           fileName = r.group(1)
       return fileName
    • 请求的http大概格式是这样(当我们访问http://localhost:9876/html/index.html时)

      GET /html/index.html HTTP/1.1
      Host: localhost:9876
      Connection: keep-alive
      Cache-Control: max-age=0
      Upgrade-Insecure-Requests: 1
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36

      第一行就是我们请求的静态资源,我们将其通过非贪婪模式的正则表达式扣出来。

  4. 在解析到请求的静态地址后就是简单的读取请求的文件,然后已http协议的格式返回回去就行了。

    def writeHtml(client, fileName):
       rspHead = None
       rspBody = None
       if not os.path.exists(fileName):
           rspHead = "HTTP/1.1 404 error\r\nServer: foreverServer\r\n\r\n"
           rspBody = "file not found"
       else:
           rspHead = "HTTP/1.1 200 OK\r\nServer: foreverServer\r\n\r\n"
           html = open(fileName, 'r', encoding='UTF-8')
           rspBody = html.read()
       client.send((rspHead + rspBody).encode("utf-8"))
    • 当请求的静态文件不存在时,将返回给客户端文件不存在。

    • 上面的相应格式是根据http相应报文的格式而定的,否则浏览器会不识别:

      这里写图片描述

      在Windows中\r\n分别代表回车和换行,而现在在unix系统中\n就代表了回车换行。

完整代码
  • 下面是完整的服务代码,不到60行就可以完成一个简单的静态web服务器,这就是python的魅力:

    from socket import *
    from threading import Thread
    import os
    import re
    
    def get_request_name_from_http(http):
      r = re.search(r"GET /(.+?) ", http)
      fileName = ""
      if r != None:
          fileName = r.group(1)
      return fileName
    
    def writeHtml(client, fileName):
      rspHead = None
      rspBody = None
      if not os.path.exists(fileName):
          rspHead = "HTTP/1.1 404 error\r\nServer: foreverServer\r\n\r\n"
          rspBody = "file not found"
      else:
          rspHead = "HTTP/1.1 200 OK\r\nServer: foreverServer\r\n\r\n"
          html = open(fileName, 'r', encoding='UTF-8')
          rspBody = html.read()
      client.send((rspHead + rspBody).encode("utf-8"))
    
    
    def deal_socket(client):
      print("-------开启新的线程----------")
      try:
          data = client.recv(1024)
          if len(data) > 0:
              fileName = get_request_name_from_http(data.decode("utf-8"))
              writeHtml(client, fileName)
    
      finally:
          client.close()
          print("-------关闭新的线程----------")
    
    
    def main():
      server = socket(AF_INET, SOCK_STREAM)
      server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
      address = ('', 9876)
      server.bind(address)
      server.listen(10)
      try:
          while True:
              print("-------等待接受服务----------")
              client, client_address = server.accept()
              print("-------接受服务成功----------")
              # 就这里和多线程不同,并且千万不能把client关掉
              p = Thread(target=deal_socket, args=(client,))
              p.start()
      except Exception as e:
          print(e)
      finally:
          server.close()
          print("-------服务结束----------")
    
    if __name__ == "__main__":
      main()
  • 到此就用python构建了史上最挫的静态wen服务器了,直接在浏览其输入静态html请求就可以显示网页了:

    这里写图片描述

    虽然很挫,不过web服务器的基本原理就是如此,牛逼的服务器也只是在这之上做了很多完善,下一篇我们将采用python提供的WSGI标准完成一个动态的web框架。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值