面向對象封裝的web服務器
阿新 • • 發佈:2018-05-12
print 請求 return 通過 int 數據 sys.argv 重用 self.
import socket import re import os import sys # 由於前面太繁瑣,可以用類封裝一下,也可以分幾個模塊 class HttpServer(object): def __init__(self,port): # 1、服務器創建負責監聽的socket self.socket_watch = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2、設置地址重用 self.socket_watch.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 3、綁定監聽的端口 self.socket_watch.bind((‘‘, port)) # 4、設置監聽隊列 self.socket_watch.listen(128) def handle_client(self,socket_con): """ 接收來自客戶端的請求,並接收請求報文,解析,返回 """ # 1、服務器接收客戶端的請求報文 request = socket_con.recv(4096).decode() # 2、截取請求報文,獲取請求行 request_lines = request.split("\r\n") # 3、獲取請求行 request_line = request_lines[0] # GET /a/ab/c.html HTTP/1.1 # 通過正則表達式 匹配出請求行中請求資源路徑 res = re.match(r"\w+\s+(\S+)",request_line) # 獲取資源路徑 path = res.group(1) # 將資源路徑和我的web文件夾的絕對路徑拼接(自己填寫) path ="# 本地絕對路徑" + path # 在判斷是文件還是文件夾之前,首先要判斷你這個路徑在服務器中是否存在 if not os.path.exists(path): response_line = ‘HTTP/1.1 404 Not Found\r\n‘ response_head = ‘Server:skylark 2.0\r\n‘ response_head += ‘Content-type:text/html;charset=utf-8\r\n‘ response_body = ‘你請求‘+ path +‘不存在‘ response = response_line + response_head + ‘\r\n‘ +response_body socket_con.send(response.encode()) socket_con.close() return else: # 判斷用戶請求的是文件還是文件夾 if os.path.isfile(path): # 如果文件存在 讀取頁面數據,然後返回 response_line = "HTTP/1.1 200 OK\r\n" response_head = "Server:skylark 2.0\r\n" # 註意請求圖片需要使用"rb"的方式進行讀取 file = open(path,"rb") # response_body 是二進制所以不用再次編碼 response_body = file.read() response = response_line.encode() + response_head.encode() +"\r\n".encode() +response_body socket_con.send(response) socket_con.close() return else: if path.endswith("/"): # 例如 www.baidu.com/images # 用戶請求的文件夾 # 1、判斷該文件夾下是否有默認的文件,如果有,則返回,如果沒有 # index.html default.html default_document = False # 如果允許你訪問我目錄下的默認文檔 if default_document: # 判斷用戶訪問的文件夾下是否有index.html 或者 default.html if os.path.exists(path + ‘/index.html‘): response_line = ‘HTTP/1.1 200 OK\r\n‘ response_head = ‘Server:skylark 2.0\r\n‘ file = open(path+‘/index.html‘, ‘rb‘) response_body = file.read() response = response_line.encode() + response_head.encode() +‘\r\n‘.encode()+response_body socket_con.send(response) socket_con.close() return elif os.path.exists(path + ‘/default.html‘): response_line = ‘HTTP/1.1 200 OK\r\n‘ response_head = ‘Server:skylark 2.0\r\n‘ file = open(path + ‘/default.html‘, ‘rb‘) response_body = file.read() response = response_line.encode() + response_head.encode() + ‘\r\n‘.encode() + response_body socket_con.send(response) socket_con.close() return else: # 訪問的目錄下,既沒有index.html 也沒有default.html response_line = ‘HTTP/1.1 404 Not Found\r\n‘ response_head = ‘Server:skylark 2.0\r\n‘ response_head += ‘Content-Type:text/html;charset=utf-8\r\n‘ response_body = ‘index.html 或者 default.html 不存在‘ response = response_line +response_head +‘\r\n‘ +response_body socket_con.send(response.encode()) socket_con.close() # 2、判斷服務器是否開啟了目錄瀏覽 else: # 判斷你是否開啟了目錄瀏覽 dir_browsing = True if dir_browsing: # 把用戶請求的文件夾中所有的文件和文件夾以目錄的形式返回到頁面中 # 獲取用戶請求的文件夾 list_names = os.listdir(path) response_line = ‘HTTP/1.1 200 OK\r\n‘ response_head = ‘Server:skylark 2.0\r\n‘ # 動態的拼接頁面,將目錄中的文件或者文件夾的名稱以HTML頁面的方式返回給瀏覽器 response_body = ‘<html><head><body><ul>‘ for item in list_names: response_body +="<li><a href = ‘#‘>"+item+"</a></li>" response_body+=‘</ul></body></head></html>‘ response =response_line + response_head +‘\r\n‘ +response_body socket_con.send(response.encode()) socket_con.close() return else: # 用戶請求的路徑沒有斜線 # 重定向到+斜線的目錄下 response_line = ‘HTTP/1.1 302 Found\r\n‘ response_head = ‘Server:skylark 2.0\r\n‘ response_body = ‘redirect‘+ path +‘/‘ response = response_line +response_head +‘\r\n‘ +response_body socket_con.send(response.encode()) socket_con.close() def run_server(self): # 5、通過循環,不停的接收來自客戶端的連接請求 while True: socket_con, con_adds = self.socket_watch.accept() # 註意將con_adds轉成字符串 print(‘客戶端:%s連接成功!!!‘ % str(con_adds)) # 接收來自客戶端的請求,並接收請求報文,解析,返回 self.handle_client(socket_con) def main(): # sys.argv方法的用法如下: # 在終端輸入 python3 面向對象封裝的web服務器.py 8888 # 在使用解釋器執行任意py文件的時候,可以傳入不止一個參數,會以字符串的形式用列表保存起來 # 但是列表的第一個參數[0]位是它自己。所以傳入的參數是從[1]第二位開始的 # 所以在上面輸入8888以後,調取這個列表的[1]下標就會傳入這個8888作為進到下面的代碼 # 再轉換一下類型為int就相當於用戶指定端口了 port = int(sys.argv[1]) http_server = HttpServer(port) http_server.run_server() if __name__ == ‘__main__‘: main()
面向對象封裝的web服務器