HTTP服務器開發項目(Python)

完整源代碼 https://github.com/skyerhxx/HttpServer

 

簡易HTTP服務器開發項目

開發環境

      Python 3.7

      IDE:Pycharm

功能

  • 能訪問 127.0.0.1:9999
  • 能訪問 127.0.0.1:9999/index.html
  • 能夠用戶名和密碼登錄127.0.0.1:9999/index.html,然後頁面會自動跳轉到另一個頁面

 

最終項目目錄結構

 

http服務器是用到了TCP協議和HTTP協議
http server是在TCP server的基礎上加了一些功能,這也正好對應了HTTP協議是TCP協議的上層

 

我們說的Web服務器就是HTTP服務器

 

 

 

傳輸層TCP協議

https://blog.csdn.net/hxxjxw/article/details/105896043

 

面向TCP協議的套接字服務端編程

實現TCPServer和StreamRequestHandler

 

實現網絡服務器TCPServer類

初始化 服務器地址、處理請求類、套接字

啓動服務器
    接受請求
    處理請求
    關閉連接

關閉服務器

socket_server.py

#實現TCPServer類

import socket

class TCPServer:

    def __init__(self,server_address,handler_class):
        self.server_address = server_address
        self.HandlerClass = handler_class #處理請求的類
        self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.is_shutdown = False
    
    #服務器的啓動函數
    def serve_forever(self):
        self.socket.bind(self.server_address) #綁定服務器地址
        self.socket.listen(10) #啓動並監聽
        #while True:
        while not self.is_shutdown:
            #1.接收請求
            request, client_address = self.get_request() 
            #2.處理請求
            try:
                self.process_request(request, client_address)
            except Exception as e:
                print(e)
            finally:
                #3.關閉連接
                self.close_request(request)
        

    #接受請求
    def get_request(self):
        return self.socket.accept()

    #處理請求
    def process_request(self, request, client_address):
        handler = self.HandlerClass(request, client_address)
        handler.handle()


    #關閉請求 
    def close_request(self,request):
        request.shutdown()
        request.close()
    
    #關閉服務器
    def shutdown(self):
        self.is_shutdown = True

 

實現網絡請求處理器Handler類

轉換字節碼用一個bytes()就行了

緩存是一個自己定義的list

 

base_handler.py

# -*- encoding=utf-8 -*-


class BaseRequestHandler:
    def __init__(self, server, request, client_address):
        self.server = server
        self.request = request
        self.client_address = client_address

    def handle(self): #不需要做任何工作,主要由後面繼承它的類去做相關工作
        pass

#功能:編碼、解碼、讀寫消息
class StreamRequestHandler(BaseRequestHandler):

    def __init__(self, server, request, client_address):
        BaseRequestHandler.__init__(self, server, request, client_address)

        self.rfile = self.request.makefile('rb')
        self.wfile = self.request.makefile('wb')
        self.wbuf = []

    # 編碼
    # 字符串—>字節碼
    def encode(self, msg):
        if not isinstance(msg, bytes):  #如果不是字節碼就編碼成字節碼
            msg = bytes(msg, encoding='utf-8')
        return msg

    # 解碼
    # 字節碼—>字符串
    def decode(self, msg):
        if isinstance(msg, bytes):
            msg = msg.decode()
        return msg

    # 讀消息
    def read(self, length):
        msg = self.rfile.read(length)
        return self.decode(msg)

    # 讀取一行消息
    def readline(self, length=65536):  #65536是http請求報文的最大長度
        msg = self.rfile.readline(length).strip()
        return self.decode(msg)

    # 寫消息
    #接受內容,然後寫到緩存裏面
    def write_content(self, msg):
        msg = self.encode(msg)  #把字符串轉成字節碼
        self.wbuf.append(msg)

    # 發送消息
    def send(self):
        for line in self.wbuf:
            self.wfile.write(line)
        self.wfile.flush()
        self.wbuf = []  #發送完之後清空緩衝區

    def close(self):
        self.wfile.close()
        self.rfile.close()

編寫網絡服務器的測試用例

測試前面實現的TCPServer和Handler是否可以正常工作

編寫客戶端來連接服務端,測試服務端是否可以正常工作

 

改進成多線程

 

此時項目結構

 

應用層HTTP協議

https://blog.csdn.net/hxxjxw/article/details/105896043

 

HTTP服務器實現

實現一個支持HTTP協議的Web服務器

Handler纔是真正處理HTTP請求的處理的類

 

實現BaseHTTPRequestHandler

繼承base_handler

HTTP請求報文格式

base_http_handler.py


import logging


from handler.base_handler import StreamRequestHandler

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

class BaseHTTPRequestHandler(StreamRequestHandler):

    def __init__(self, server, request, client_address):
        self.method = None
        self.path = None
        self.version = None
        self.headers = None
        self.body = None
        StreamRequestHandler.__init__(self,server, request, client_address)

    #請求的處理
    def handle(self):
        try:
            #1、解析請求
            if not self.parse_request():
                return
            #2、方法執行(GET、POST)
            method_name = 'do_' + self.method
            #自檢判斷方法是否存在
            if not hasattr(self,method_name):
                #發送錯誤
                return
            method = getattr(self,method_name)
            method() #應答報文的封裝
            #3、發送結果
            self.send()
        except Exception as e:
            logging.exception(e)


    #解析請求頭
    def parse_headers(self):
        #請求頭是以key:value的形式存在的
        headers = {}
        while True:
            line = self.readline()
            #如果是空行,表示請求頭已經結束
            if line:
                key,value = line.split(":",1)  #這個1是分割次數,就是指分割一次
                key = key.strip()
                value = value.strip()
                #將key,value保存到map中
                headers[key] = value
            else:
                break
        return headers


    #解析請求
    def parse_request(self):
        #1、解析請求行
        #讀取第一行————請求行
        first_line = self.readline()
        words = first_line.split()
        #請求方法、請求地址、請求的HTTP版本
        self.method, self.path, self.version = words

        #2、解析請求頭
        self.headers = self.parse_headers()

        #3、解析請求內容
        #如果請求頭有內容,那麼它的長度將會保存在headers裏面
        key = 'Content-Length'
        if key in self.headers.keys():
            #請求內容的長度
            body_length = int(self.headers[key])
            self.body = self.read(body_length)

        return True

 

HTTP應答報文

 

編寫基礎HTTP服務器測試工作,測試其是否正常工作

新建base_http_server.py

在原來的test.py的基礎上

 

 

此時,用瀏覽器訪問localhost:9999

 

添加了個do_GET方法

可以看到這些我們寫的內容都能正常返回

 

到目前爲止,我們已經把一個基礎的web服務器搭建起來了,這個服務器可以

具備這三個功能,一個基礎的web服務器就已經實現了,我們項目的目的也已經達到了

此時的目錄結構

 

即HttpServer4就是一個可以實現基本功能的http服務器了

 

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

 

但是爲了更好的理解GET方法以及POST方法的工作原理

我們來編寫自定義HTTP應用之GET方法和POST方法

編寫自定義HTTP應用之GET方法

web服務器是怎樣把html頁面以及圖片返回給瀏覽器的,以及怎樣進行賬號和密碼的校驗

我們來看webserver是怎樣把資源/數據返回給瀏覽器的

新建simple_http_handler.py

 

編寫測試用例,看是否可用

新建simple_http_server.py

 

如果請求一個不存在的資源

GET方法
首先判斷資源是否存在
不存在直接返回404,存在的話,首先把資源的文件打開,讀取文件的長度,寫到頭部,再把內容讀取出來,寫到緩衝區。就是一行一行的讀。返回瀏覽器之所以能成網頁的界面是因爲要讀取的那個文件就是html的,http服務器並沒有怎麼處理

 

編寫自定義HTTP應用之POST方法

POST就是提交數據到服務器端進行處理,這裏以賬號密碼爲例

來看數據提交到後臺之後,後臺是怎麼來處理的

提交是用js來提交的

正確的用戶名密碼是 hxx  123456

給他設置的是登錄成功後會自動跳轉到我的博客

如果是錯誤的

 

至此,項目完結

此時項目結構

 

 

未來可以增添的功能

功能方面

①更完善的異常處理機制

      狀態碼只利用了404狀態碼,還有很多其他的沒有用

②更豐富的功能支持

    日誌、長連接、https安全協議

性能方面

①引入線程池

      否則頻繁創建和銷燬線程會對性能造成一定的影響

②不使用多線程

      用事件驅動引擎(epoll、select),改造成事件驅動的web服務器

 

 

 

參考:

https://www.imooc.com/learn/1172

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章