结构最清晰的HTTP服务器server和后端框架搭建(完整代码/HTTP/套接字/网页/并发)

HTTPServer项目设计文档

目录

文章目录

  • HTTPServer项目设计文档
    • 目录
    • 功能要求
        • httpserver部分
        • WebFrame部分
    • 需求分析
      • 框架搭建
      • 技术点
        • 技术点--json
        • 技术点--正则表达式
        • 技术点--并发处理
        • 交互数据格式协议
    • 程序代码结构
      • 主要模块结构
      • server端
        • server
        • config--server
      • webframe端
        • webframe
        • setting
        • static(非模块,存放静态网页)
        • url
        • views
      • 完整代码
        • httpserver
        • webframe

功能要求

httpserver部分

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

WebFrame部分

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

需求分析

框架搭建

  1. httpserver和应用处理分离的模式:降低了耦合度,逻辑独立性(一个模块处理一个逻辑)
  2. 用户配置文件:封装内部结构,用户友好
  3. webframe部分采用了模拟后端框架的处理方法
    结构最清晰的HTTP服务器server和后端框架搭建(完整代码/HTTP/套接字/网页/并发)_第1张图片
    结构最清晰的HTTP服务器server和后端框架搭建(完整代码/HTTP/套接字/网页/并发)_第2张图片
    主要文件部署结构
project
httpserver
WebFrame
HttpServer.py 服务区主程序
config httpserver配置
WebFrame.py 后端主程序
static 存放静态网页
views.py 应用处理程序
urls.py 存放路由
settings 框架配置

技术点

  1. httpserver部分需要与两端建立通信:浏览器请求接受和响应回复,后端的请求发送/结果接收
  2. webFrame部分采用多路复用接收并发请求
  3. 数据传递使用json格式,使用python jasn

技术点–json

dict_={"name":"梅梅","age":20,"height":168,"gender":"女"}
# 打包:python数据---> json,支持转换python数据格式:dict,list
jdata=json.dumps(dict_)
# 解析:json格式数据---> python
py_data=json.loads(jdata)

技术点–正则表达式

re=r"[A-Z]+\s+\S*"
re=r"(?P[A-Z]+)\s+(?P\S*)"

技术点–并发处理

方案 实现方法
多进程 os模块fork()实现多进程:
multiprocessing模块Process实现多进程:
1.为单独的入口函数开辟单独进程
2.自定义进程类,run定义启动函数,可以将共同使用的数据封装为类实例变量
多线程 multiprocessing模块Tread实现多进程:
1.与Process进程模块使用方法相同
2.注意区别python线程和进程的区别
IO多路复用 IO多路复用适合处理IO请求密集型问题,对于长期占用服务处理逻辑需要用多进程/线程
select–rs,ws,xs=select(rlist,wlist,xlist) rlist监听读属性IO,连接结束删除监听对象
poll—fdmap存放文件描述符与IO对象字典,register添加监听对象
epoll-- 与poll使用方法相同,但内部实现机制不同,epoll效率最高,可设置边缘触发

httpserver–多线程

webframe–IO多路复用epoll

交互数据格式协议

httpserver-->webframe  {method:'GET',info:'/'}
webframe-->httpserver {status:'200',data:'ccccc'}

程序代码结构

主要模块结构

server端

server

基本功能 函数 数据交互方向 数据格式
1.获取http请求 handle() server<–client http请求
2.解析http请求 handle() server http–>json
3.将请求发送给WebFrame connect_webframe() server–>webframe json
4.从WebFrame接收反馈数据 connect_webframe() server<–webframe json
5.将数据组织为响应格式发送客户端 response() server–>client http响应
"""
HttpServer         
"""
from server.config import *
# httpserver功能
class HTTPServer():
    #初始化变量,并调用套接字创建和绑定程序
    # 创建套接字
    def create_socket(self):
    # 套接字地址绑定
    def bind(self):
    # 启动服务,循环接收网页连接,并分配子线程处理
    def server_forever(self):
    # 线程主处理函数
    def handle(self, connfd):
    # 组织对网页请求的具体响应内容
    def response(self, connfd, data):
# 链接后端,并将来自浏览器的请求转发给后端,接受后端发送的内容
def connect_webframe(req):

if __name__ == "__main__":
    httpd = HTTPServer()
    httpd.server_forever()

config–server

"""
httpserver相关配置 需要使用者进行配置
"""
# [httpserver address]
HOST="0.0.0.0"
PORT=8000
# [Debug]
DEBUG=1
# [webframe address]
frame_ip="127.0.0.1"
frame_port=8080

webframe端

webframe

"""
从httpserver接收具体请求
根据请求进行逻辑处理和数据处理
将需要的数据反馈给httpserver
"""
from setting import *
from urls import *
# 应用类实现具体应用功能
class Application():
    #基本变量初始化
    def __init__(self):
    # 创建监听套接字
    def create_socket(self):
    # 绑定地址
    def bind(self):
    # 初始化epoll文件符-->IO对象映射地图
    def init_epoll(self):
    # 服务启动(IO多路复用,epoll())
    def start(self):
    # 逻辑处理请求,发送请求内容
    def handle(self,connfd):
    # 获取响应内容(网页)
    def get_html(self,info):
    #获取响应数据(其他类型)
    def get_data(self,info):
if __name__=="__main__":
    app=Application()
    app.start()

setting

# 后端套接字地址
# [webframe address]
frame_host="0.0.0.0"
frame_port=8080
# [Debug]
DEBUG=1
# [static html]
STATIC_DIR="/home/tarena/leixing/code_of_all/projects/http3d0/frame/static"

static(非模块,存放静态网页)

404.html
index.html

url

from views import *
urls={
    # 非网页请求,请求与函数映射关系
    "/time":times,
    "/guonei":guonei,
    "/guowai":guowai
}

views

from time import *
# 非网页请求函数实现
def times():
    return ctime()
def guonei():
    return "guo nei news:hao hao hao!"
def guowai():
    return "guo wai news:can can can!"

完整代码

httpserver

from socket import *
import os, sys
from threading import Thread
import json
from projects.http3d0.server.config import *
import re

# httpserver功能
class HTTPServer():
    #初始化变量,并调用套接字创建和绑定程序
    def __init__(self):
        self.host = HOST
        self.port = PORT
        self.create_socket()
        self.bind()
    # 创建套接字
    def create_socket(self):
        self.socketfd = socket()
        self.socketfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, DEBUG)
    # 套接字地址绑定
    def bind(self):
        self.address = (self.host, self.port)
        self.socketfd.bind(self.address)
    # 启动服务,循环接收网页连接,并分配子线程处理
    def server_forever(self):
        self.socketfd.listen(5)
        print("Start httpserver:%d" % self.port)
        while True:
            connfd, addr = self.socketfd.accept()
            client = Thread(target=self.handle, args=(connfd,))
            client.setDaemon(True)
            client.start()
    # 线程主处理函数
    def handle(self, connfd):
        # 接受网页请求
        request = connfd.recv(1024).decode()
        # 解析请求正则表达式
        pattern = r"(?P[A-Z]+)\s+(?P/\S*)"
        try:
            # {'method': 'GET', 'info': '/'}  <--- GET / HTTP/1.1
            env = re.match(pattern, request).groupdict()
        except:
            connfd.close()
            return
        else:
            # data是从webframe返回的请求内容数据
            data = connect_webframe(env)

            data = json.loads(data)
            self.response(connfd, data)
    # 组织对网页请求的具体响应内容
    def response(self, connfd, data):
        # data-->{'sattus':'200','data':'XXX'}
        print(data)
        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 NotFound\r\n"
            responseHeaders += "Content-Type:text/html\r\n"
            responseHeaders += "\r\n"
            responseBody = data['data']
        response = responseHeaders + responseBody
        print("RES:", response)
        connfd.send(response.encode())

# 链接后端,并将来自浏览器的请求转发给后端,接受后端发送的内容
def connect_webframe(req):
    s = socket()
    try:
        s.connect((frame_ip, frame_port))
    except Exception as e:
        print(e)
        return
    # 将内容转化为json,并发送给后端
    data = json.dumps(req)
    s.send(data.encode())
    # 接收webframe发送的内容
    wfdata = s.recv(10 * 1024 * 1024).decode()
    return wfdata

if __name__ == "__main__":
    httpd = HTTPServer()
    httpd.server_forever()

webframe


from socket import *
import json
from setting import *
import os,sys
from select import *
from urls import *
# 应用类实现具体应用功能
class Application():
    #基本变量初始化
    def __init__(self):
        self.host = frame_host
        self.port = frame_port
        self.create_socket()
        self.bind()
        self.init_epoll()
    # 创建监听套接字
    def create_socket(self):
        self.appfd=socket()
        self.appfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,DEBUG)
    # 绑定地址
    def bind(self):
        self.ADDR=(self.host,self.port)
        self.appfd.bind(self.ADDR)
        self.appfd.listen(5)
        print("Listen from port:%d"%self.port)
        
    # 初始化epoll文件符-->IO对象映射地图
    def init_epoll(self):
        self.fdmap={}
        self.ep=epoll()
        # 关注套接字,并添加文件描述符(每个IO对象唯一)map
        # 关注对象和文件符字典始终统一
        self.ep.register(self.appfd,EPOLLIN)
        self.fdmap[self.appfd.fileno()]=self.appfd
        
    # 服务启动(IO多路复用,epoll())
    def start(self):
        while True:
            events=self.ep.poll()
            for fileno,event in events:
                if fileno==self.appfd.fileno():
                    connfd,addr=self.fdmap[fileno].accept()
                    self.ep.register(connfd)
                    self.fdmap[connfd.fileno()]=connfd
                else:
                    self.handle(self.fdmap[fileno])
                    # 取消关注,删除地图对应
                    self.ep.unregister(fileno)
                    del self.fdmap[fileno]
                    
    # 逻辑处理请求,发送请求内容
    def handle(self,connfd):
        request=connfd.recv(2048).decode()
        request=json.loads(request)
        print(request)
        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

        # 网页转化为json格式并发送给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 in urls:
            if info==url:
                return {"status": "200", "data": urls[info]()}
        return {"status":"404","data":"Request out of range,Only for adults"}

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

你可能感兴趣的:(网络编程)