- HTTP 請求報文格式:
Host: 127.0.0.1:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
- HTTP 響應報文格式:
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 14 Mar 2018 09:52:48 GMT
Server: BWS/1.1
採用 TCP 協議創建 socket 套接字對象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 複用端口
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 綁定 IP 和端口
server_socket.bind(('', 8888))
# 開啓監聽,由主動模式變爲被動模式
server_socket.listen()
等待客戶端的連接,並接收客戶端請求數據
# 等待客戶端連接
new_socket, client_addr = server_socket.accept()
# 接收瀏覽器發送的請求
recv_data = new_socket.recv(1024).decode()
# 防止客戶端下線導致bug
if not recv_data:
print("客戶端下線!")
new_socket.close()
continue
解析請求並返回數據
# 從請求行獲取路徑信息
data_list = recv_data.splitlines()
request_line = data_list[0]
regex = re.match(r'.* (.*) .*', request_line)
file_path = regex.group(1)
if file_path == '/':
file_path = '/myWeChat.html'
try:
# 採用二進制讀取本地文件內容(方便讀取圖片等非文本文件)
with open(f'.{file_path}', 'rb') as f:
response_body = f.read()
except Exception as reason:
# 沒有相關文件資源時返回 404 錯誤
response_line = "HTTP/1.1 404 Not Found\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
response_body = f'<h2>404 {reason}</h2>'.encode()
else:
# 解析請求
response_line = "HTTP/1.1 200 OK\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
finally:
# 響應請求並返回數據
response = response_line + response_headers
new_socket.send(response.encode() + response_body)
# 關閉
new_socket.close()
# 從請求行獲取路徑信息
data_list = recv_data.splitlines()
request_line = data_list[0]
regex = re.match(r'.* (.*) .*', request_line)
file_path = regex.group(1)
該代碼可以顯示指定頁面
if file_path == '/':
file_path = '/myWeChat.html'
該代碼可以設置默認首頁
完整代碼:
import re
import socket
if __name__ == '__main__':
"""返回固定的頁面數據"""
# 採用 TCP 協議創建 socket 對象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 使端口重複使用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 綁定 IP 和端口
server_socket.bind(('', 8888))
# 開啓監聽,由主動模式變爲被動模式
server_socket.listen()
while True:
# 等待客戶端連接
new_socket, client_addr = server_socket.accept()
# 接收瀏覽器發送的請求
recv_data = new_socket.recv(1024).decode()
# 防止客戶端下線導致bug
if not recv_data:
print("客戶端下線!")
new_socket.close()
continue
# 從請求行獲取路徑信息
data_list = recv_data.splitlines()
request_line = data_list[0]
regex = re.match(r'.* (.*) .*', request_line)
file_path = regex.group(1)
if file_path == '/':
file_path = '/myWeChat.html'
try:
# 採用二進制讀取本地文件內容(方便讀取圖片等非文本文件)
with open(f'.{file_path}', 'rb') as f:
response_body = f.read()
except Exception as reason:
response_line = "HTTP/1.1 404 Not Found\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
response_body = f'<h2>404 {reason}</h2>'.encode()
else:
# 解析請求
response_line = "HTTP/1.1 200 OK\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
finally:
# 響應請求並返回數據
response = response_line + response_headers
new_socket.send(response.encode() + response_body)
# 關閉
new_socket.close()
一個 web 服務器開發完了。等等,這個服務器好像有點問題,當一個客戶端連接時其他客戶端就沒法連接了。我開發服務器並不是爲一個人服務,開發服務器也要符合社會主義核心價值觀,要爲人民服務。
通過多協程實現多用戶處理
import re
import socket
from gevent import monkey
import gevent
# 可以簡單理解爲將一些模塊變爲非阻塞,具體解釋需要上網查閱資料
monkey.patch_all()
def handle_request(new_socket):
# 接收瀏覽器發送的請求
recv_data = new_socket.recv(1024).decode()
# 防止客戶端下線導致bug
if not recv_data:
print("客戶端下線!")
new_socket.close()
return
# 從請求行獲取路徑信息
data_list = recv_data.splitlines()
request_line = data_list[0]
regex = re.match(r'.* (.*) .*', request_line)
file_path = regex.group(1)
if file_path == '/':
file_path = '/myWeChat.html'
try:
# 採用二進制讀取本地文件內容(方便讀取圖片等非文本文件)
with open(f'.{file_path}', 'rb') as f:
response_body = f.read()
except Exception as reason:
response_line = "HTTP/1.1 404 Not Found\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
response_body = f'<h2>404 {reason}</h2>'.encode()
else:
# 解析請求
response_line = "HTTP/1.1 200 OK\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
finally:
# 響應請求並返回數據
response = response_line + response_headers
new_socket.send(response.encode() + response_body)
# 關閉
new_socket.close()
def main():
"""程序主入口,實現 web 服務器功能"""
# 採用 TCP 協議創建 socket 對象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 使端口重複使用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 綁定 IP 和端口
server_socket.bind(('', 8888))
# 開啓監聽,由主動模式變爲被動模式
server_socket.listen()
while True:
# 等待客戶端連接
new_socket, client_addr = server_socket.accept()
gevent.spawn(handle_request, new_socket)
if __name__ == '__main__':
main()
總感覺還是差了點什麼,哦,對了,python 是面嚮對象語言,這個代碼沒有對象啊,現實中沒有對象,寫個代碼還沒有對象,一萬點暴擊傷害。下面來個終極版的面向對象封裝
import re
import socket
from gevent import monkey
import gevent
monkey.patch_all() # 可以簡單理解爲使一些模塊變爲非阻塞,具體原理可以上網查詢資料
class WebServer:
def __init__(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.server_socket.bind(('', 8888))
self.server_socket.listen()
def handle_request(self, new_socket):
"""處理請求並完成響應操作"""
# 接收瀏覽器發送的請求
recv_data = new_socket.recv(1024).decode()
# 防止客戶端下線導致bug
if not recv_data:
print("客戶端下線!")
new_socket.close()
return
# 從請求行獲取路徑信息
data_list = recv_data.splitlines()
request_line = data_list[0]
regex = re.match(r'.* (.*) .*', request_line)
file_path = regex.group(1)
if file_path == '/':
file_path = '/myWeChat.html'
try:
# 採用二進制讀取本地文件內容(方便讀取圖片等非文本文件)
with open(f'.{file_path}', 'rb') as f:
response_body = f.read()
except Exception as reason:
response_line = "HTTP/1.1 404 Not Found\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
response_body = f'<h2>404 {reason}</h2>'.encode()
else:
# 解析請求
response_line = "HTTP/1.1 200 OK\r\n"
response_headers = "Server: WASPVAE/6.6\r\n"
response_headers += "Connection: Keep_Alive\r\n"
response_headers += "Content-Type: text/html;charset=utf-8\r\n" # 解決中文亂碼問題
response_headers += "\r\n"
finally:
# 響應請求並返回數據
response = response_line + response_headers
new_socket.send(response.encode() + response_body)
# 關閉
new_socket.close()
def run(self):
"""運行服務器,接收客戶端的請求,處理請求並完成響應操作"""
while True:
# 等待客戶端連接
new_socket, client_addr = self.server_socket.accept()
gevent.spawn(self.handle_request, new_socket)
def main():
"""程序主入口,實現 web 服務器功能"""
web_server = WebServer()
web_server.run()
if __name__ == '__main__':
main()
可以在項目當前文件夾添加幾個 html 文件在瀏覽器輸入 localhost:8888/xxx.html 或者 127.0.0.1:8888/xxx.html測試下
有興趣的朋友可以關注下我的微信個人訂閱號python數據之路,裏面有我之前自學 python 的一些資料和在黑馬學習的心得與筆記。