mmxingye

导航

06 | python HTTPServer(入门)

目标:理解 httpserver 开发的流程 ,为后面学习成熟的框架打好基础。理解配置文件这种与用户交互的方式。理解 httpserver 和 webframe 两部分的作用。 复习 多线程并发 和 IO 多路复用。理解 功能类的封装。理解 网站请求有 页面和数据两类。


功能 :

httpserver部分

获取http请求
解析http请求
将请求发送给WebFrame
从WebFrame接收反馈数据
将数据组织为Response格式发送给客户端

WebFrame部分

从httpserver接收具体请求
根据请求进行逻辑处理和数据处理
将需要的数据反馈给httpserver

特点

采用httpserver和应用处理分离的模式,降低了耦合度
采用了用户配置文件的思路
webframe部分采用了模拟后端框架的处理方法

技术点

httpserver部分需要与两端建立通信
webFrame部分采用多路复用接收并发请求
数据传递使用json格式

项目结构:

           |--httpserver --HttpServer.py (主程序)      
           |             --config (httpserver配置)   
  project--|
           |
           |
           |--WebFrame   --WebFrame.py (主程序代码)
                         --static (存放静态网页)
                         --views.py ( 应用处理程序) 
                         --urls.py (存放路由)
                         --settings (框架配置)

交互数据格式协议

httpserver-->webframe  {method:'GET',info:'/'}

webframe-->httpserver {status:'200',data:'ccccc'}

🔧HTTPserver代码

"""
config.py
http server 相关配置
需要使用者提供的内容写在配置文件
"""

# [http server address]
HOST = '0.0.0.0'
PORT = 8000

# [debug]
DEBUG = True

# web frame地址
frame_ip = '127.0.0.1'
frame_port = 8080
"""
httpserver.py

httpserver 3.0
获取http请求
解析http请求
将请求发送给WebFrame
从WebFrame接收反馈数据
将数据组织为Response格式发送给客户端
"""

from socket import *
import sys
from threading import Thread
import json,re
from config import *   # 导入配置文件内容

# 负责和webframe交互, socket客户端
def connect_frame(env):
    s = socket()
    try:
        s.connect((frame_ip,frame_port))
    except Exception as e:
        print(e)
        return

    # 将env转换为json发送
    data = json.dumps(env)
    s.send(data.encode())
    # 接收webframe反馈的数据
    data = s.recv(1024 * 1024 * 10).decode()
    return json.loads(data)


# httpserver功能
class HTTPServer:
    def __init__(self):
        self.host = HOST
        self.port = PORT
        self.create_socket()
        self.bind()

    # 创建套接字
    def create_socket(self):
        self.sockfd = socket()
        self.sockfd.setsockopt(SOL_SOCKET,      # 是否端口立即重用
                               SO_REUSEADDR,
                               DEBUG)
    # 绑定地址
    def bind(self):
        self.address = (self.host,self.port)
        self.sockfd.bind(self.address)

    # 启动服务
    def serve_forever(self):
        self.sockfd.listen(5)
        print("Start the http server:%d"%self.port)
        while True:
            connfd,addr = self.sockfd.accept()
            client = Thread(target=self.handle,
                            args=(connfd,))
            client.setDaemon(True)
            client.start()

    # 具体处理客户端请求
    def handle(self,connfd):
        request = connfd.recv(4096).decode()

        pattern=r'(?P<method>[A-Z]+)\s+(?P<info>/\S*)'
        try:
            env = re.match(pattern,request).groupdict()   # match 是从开头开始匹配防止后面也有类似内容
        except:
            connfd.close()
            return
        else:
            # data就是从webframe得到的数据
            data = connect_frame(env)
            if data:
                self.response(connfd,data)

    def response(self,connfd,data):
        # data-->{'status':'200','data':'xxx'}
        if data['status'] == '200':
            responseHeaders = "HTTP/1.1 200 OK\r\n"              
            responseHeaders += 'Content-Type:text/html\r\n'
            responseHeaders += '\r\n'
            responseBody = data['data']
        elif data['status'] == '404':
            responseHeaders = "HTTP/1.1 404 Not Found\r\n"
            responseHeaders += 'Content-Type:text/html\r\n'
            responseHeaders += '\r\n'
            responseBody = data['data']
        elif data['status'] == '302':
            pass

        # 将数据发送给浏览器
        data = responseHeaders+responseBody
        connfd.send(data.encode())


if __name__ == '__main__':
    httpd = HTTPServer()
    httpd.serve_forever() # 启动服务
#server_test.py
# 用户测试httpserver
from socket import *
import json

s = socket()
s.bind(('127.0.0.1',8080))
s.listen(5)

while True:
    c,addr = s.accept()
    data = c.recv(1024)
    print(data)
    data = json.dumps({'status':'200','data':'ccccccc'})
    c.send(data.encode())

🔧webframe 代码

"""
settings.py

web frame 部分的配置文件
"""

# [frame address]
frame_ip = '127.0.0.1'
frame_port = 8080

# [debug]
DEBUG = True

# [static]
STATIC_DIR = "./static"
"""
url.py
声明客户端能够请求的数据
"""
from views import *

urls = [
    ('/time',show_time),
    ('/guonei',guonei),
    ('/guoji',guoji)
]

"""
views.py
数据处理的具体函数
"""
import time

def show_time():
    return time.ctime()

def guonei():
    return "你看到了国内新闻"

def guoji():
    return "你看到了国际新闻"
"""
webframe.py
模拟网站的后端应用

从httpserver接收具体请求
根据请求获取网页
将需要的数据反馈给httpserver
"""

from socket import *
import json
from settings import *
from select import *
from urls import *

# 应用类,实现具体的后端功能
class Application:
    def __init__(self):
        self.host = frame_ip
        self.port = frame_port
        self.fdmap = {} # 查找地图
        self.ep = epoll() # epoll对象
        self.sockfd = socket()
        self.sockfd.setsockopt(SOL_SOCKET,
                               SO_REUSEADDR,
                               DEBUG)
        self.sockfd.bind((self.host,self.port))

    # 用于服务启动
    def start(self):
        self.sockfd.listen(5)
        print("Listen the port %d"%self.port)
        # 关注sockfd
        self.ep.register(self.sockfd,EPOLLIN)
        self.fdmap[self.sockfd.fileno()] = self.sockfd

        while True:
            events = self.ep.poll()
            for fd,event in events:
                if fd == self.sockfd.fileno():
                    connfd,addr = self.fdmap[fd].accept()
                    self.ep.register(connfd)
                    self.fdmap[connfd.fileno()] = connfd
                else:
                    self.handle(self.fdmap[fd])
                    self.ep.unregister(fd)
                    del self.fdmap[fd]

    # 具体处理请求
    def handle(self,connfd):
        request = connfd.recv(1024).decode()
        request = json.loads(request)
        # request -> {'method':'GET','info':'/'}
        if request['method'] == 'GET':
            if request['info'] == '/' or \
                    request['info'][-5:] == '.html':
                response = self.get_html(request['info'])
            else:
                response = self.get_data(request['info'])
        elif request['method'] == 'POST':
            pass
        # 将数据发送给HTTPserver
        response = json.dumps(response)
        connfd.send(response.encode())
        connfd.close()

    # 网页处理函数
    def get_html(self,info):
        if info == '/':
            filename = STATIC_DIR+'/index.html'
        else:
            filename = STATIC_DIR+info

        try:
            fd = open(filename)
        except:
            f = open(STATIC_DIR+'/404.html')
            return {'status':'404','data':f.read()}
        else:
            return {'status':'200','data':fd.read()}

    # 处理数据
    def get_data(self,info):
        for url,func in urls:
            if url == info:
                return {'status':'200','data':func()}
        return {'status':'404','data':'Sorry...'}

if __name__ == '__main__':
    app = Application()
    app.start()

posted on 2022-06-04 21:14  独立树  阅读(2253)  评论(0编辑  收藏  举报