模擬使用 socket 多線程 / io多路複用 實現一個簡單的 httpserver 和 webframe 交互的項目程序 html
指望能夠實現簡單的 以 http 協議爲標準的 和瀏覽器 / 前端框架的 數據交互前端
以及 實現簡單的 前端框架模型 python
項目分爲兩部分 httpserver 部分以及 webframe 部分web
HTTPserver 部分json
1. 獲取 http 請求後端
2. 解析 http 請求瀏覽器
3. 將請求發送給 WebFrame前端框架
4. 從 WebFrame 接收數據信息服務器
5. 將數據信息組織爲 Response 發送給客戶端多線程
WebFrame 部分
1. 從 httpserver 獲取具體請求
2. 根據請求進行邏輯或者數據處理 (簡單處理, 這裏不會過度細緻)
3. 將數據資源發送給 httpserver
兩部分整合後應實現的程度
1. httpserver 與應用程序分離, 各自獨立
2. 獨立開發, 下降互相干擾, 採用各自的配置模式
3. 在後端應用程序中優化數據的處理模型 (暫時就先不作了)
高併發需求處理
1. 多線程
2. IO 多路複用
內部實現功能
1. 做爲瀏覽器的 socket_server 須要建立------>服務器套接字 (配合多線程)
2. 接收瀏覽器的 請求進行處理 ------> 處理函數
3. 做爲 webframe 的 socket_client 須要建立------->客戶端套接字
4. 向 webframe 發送請求 -------> 處理函數
#!/user/bin/env python3 # coding=utf8 """ AID httpserver v3.0 """ from socket import * import sys import json from threading import Thread # 導入配置信息 from httpserver_config import * # 向 frame 發送請求 def connect_frame(**kwargs): s = socket() try: s.connect(frame_address) except Exception as e: print(e) return # 將請求發送給 frame s.send(json.dumps(kwargs).encode()) data = s.recv(4096).decode() return data # 封裝 httpserver 基本功能 class HTTPserver(object): def __init__(self, address): self.address = address self.create_socket() self.bind(address) # 建立套接字 def create_socket(self): self.sockfd = socket() self.sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, DEBUG) # 綁定監聽地址和端口 def bind(self, address): self.ip = address[0] self.port = address[1] self.sockfd.bind(address) # 啓動服務 def serve_forever(self): self.sockfd.listen(10) print("Listen the port %d ..." % self.port) while True: try: connfd, addr = self.sockfd.accept() print("Connect from", addr) except KeyboardInterrupt: self.sockfd.close() sys.exit("退出 httpserver 服務") except Exception as e: print(e) continue client = Thread(target=self.handle, args=(connfd,)) client.setDaemon(True) client.start() # 處理瀏覽器的 http 請求 def handle(self, connfd): request = connfd.recv(4096) # 處理客戶端斷開 if not request: connfd.close() return request_lines = request.splitlines() # 獲取請求行 request_lines = request_lines[0].decode("utf-8") print(request_lines) # 這裏能夠使用正則固然更好, 可是目前來講無大用 # 獲取請求方法, 請求內容 tmp = request_lines.split(" ") method = tmp[0] path_info = tmp[1] data = connect_frame(method=method, path_info=path_info) self.response(connfd, data) # 處理返回的數據內容 def response(self, connfd, data): # 根據狀況組織響應 if data != "404": response_headlers = "HTTP/1.1 200 OK\r\n" else: response_headlers = "HTTP/1.1 404 Not Found\r\n" response_headlers += '\r\n' response_body = data response = response_headlers + response_body connfd.send(response.encode()) connfd.close() httpd = HTTPserver(ADDR) httpd.serve_forever() # 啓動服務程序
htttpserver 的配置文件相關
""" HTTPserver 配置文件, 用戶填寫基本的必要信息 """ # HTTPserver HOST = "0.0.0.0" PORT = 8000 ADDR = (HOST, PORT) # debug 設置爲 True 表示調試 DEBUG = True # 配合 WebFrame 地址 frame_ip = "127.0.0.1" frame_port = 8080 frame_address = (frame_ip, frame_port)
內部實現功能
1. 做爲 httpserver 的服務端處理其請求 ----> 建立 socket_server ( IO多路複用 select 方式 )
2. 處理請求 ----> 對具體的 url 具體分支處理函數
#!/user/bin/env python3 # coding=utf8 """ 模擬網站後端應用處理程序 httpserver v3.0 """ from socket import * import select import json # 導入配置文件 from settings import * from views import * # 建立應用類, 用於具體處理請求 class Application(object): def __init__(self): self.ip = frame_address[0] self.port = frame_address[1] self.sockfd = socket() self.sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self.sockfd.bind(frame_address) def start(self): self.sockfd.listen(5) print("Listen the port %d" % self.port) rlist = [self.sockfd] wlist = [] xlist = [] while True: rs, ws, xs = select.select(rlist, wlist, xlist) # 循環檢查是否就緒 for r in rs: # 判斷加入 關注列表 if r is self.sockfd: connfd, addr = r.accept() rlist.append(connfd) else: # 接受 httpserver 的請求 request = r.recv(2048).decode() if not request: rlist.remove(r) continue self.handle(r, request) # 處理請求 def handle(self, connfd, request): request = json.loads(request) method = request["method"] path_info = request["path_info"] print(path_info) if method == "GET": if path_info == "/" or path_info[-5:] == ".html": data = self.get_html(path_info) else: data = self.get_data(path_info) elif method == "POST": pass # 獲得網頁內容就發送 未獲得就發送404 if data: connfd.send(data.encode()) else: connfd.send(b"404") # 處理網頁 def get_html(self, path_info): if path_info == "/": get_file = STATIC_DIR + "/index.html" else: get_file = STATIC_DIR + path_info try: fd = open(get_file, encoding="utf8") data = fd.read() except IOError: return return data # 處理數據 def get_data(self, path_info): for url, func in urls: if path_info == url: return func() return "404" app = Application() app.start() # 啓動後端框架服務
框架的配置文件
以及 自定義的函數對應方法的映射配置 相似於 Django 的路由系統
""" Frame 程序配置文件 """ from views import * # 配置框架地址 frame_ip = "0.0.0.0" frame_port = 8080 frame_address = (frame_ip, frame_port) # 靜態網頁位置 STATIC_DIR = "./static" # urls = [ ("/time", show_time), ("/hello", say_hello) ]
自定義的返回視圖函數, 相似於 Django 的 視圖系統
import time def show_time(): return time.ctime() def say_hello(): return "Hello world"