因爲網絡間通信是基於TCP協議傳輸數據的,而服務器與瀏覽器之間通信是基於HTTP協議的,那麼下面基於python實現一個多進程或多線程tcp服務器,瀏覽器可以基於http協議進行發送請求和解析。瀏覽器展示返回的一個標準的HTML網頁,此外實現服務器解析客戶端多次請求並且返回請求結果。即:客戶端根據HTML裏面的各種鏈接,再發送HTTP請求給服務器,拿到相應的圖片、視頻、Flash、JavaScript腳本、CSS等各種資源,最終顯示出一個完整的頁面。
1.代碼實現多進程HTTP服務器與測試
如下所謂多進程HTTP服務器和之前的單進程區別就是,這裏使用一個主進程接受accept()請求,但是使用多進程分別去解析,響應瀏覽器的請求。舉個簡單例子就是:之前是一個人接受請求,並且處理請求;現在是一個人負責接受請求,然後分發給不同人的人去處理請求。這樣整體來說響應和處理的速度要快很多,這既是多進程多線程在這裏的體現。因爲之前如果使用單進程單線程一旦recv()函數堵塞時,這個通信都會堵塞,現在使用多線程與多進程則解決了這個堵塞問題。
import socket
import re
import multiprocessing
def service_client(new_socket):
"""爲這個客戶端返回數據"""
# 1. 接收瀏覽器發送過來的請求 ,即http請求
# GET / HTTP/1.1
request = new_socket.recv(1024).decode("utf-8")
# print(request)
request_lines = request.splitlines()
print("")
print(">" * 20)
print(request_lines)
# GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0]) #獲取請求文件名
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html"
# 2. 返回http格式的數據,給瀏覽器
try:
f = open("./html" + file_name, "rb")
except: #對於沒有找到請求文件路徑的返回結果
response = "HTTP/1.1 404 NOT FOUND\r\n"
response += "\r\n"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 準備發送給瀏覽器的數據---header
response = "HTTP/1.1 200 OK\r\n"
response += "\r\n"
# 2.2 準備發送給瀏覽器的數據---boy
# response += "hahahhah"
# 將response header發送給瀏覽器
new_socket.send(response.encode("utf-8"))
# 將response body發送給瀏覽器
new_socket.send(html_content)
# 關閉套接
new_socket.close()
def main():
"""用來完成整體的控制"""
# 1. 創建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. 綁定
tcp_server_socket.bind(("", 7890))
# 3. 變爲監聽套接字
tcp_server_socket.listen(128)
while True:
# 4. 等待新客戶端的鏈接
new_socket, client_addr = tcp_server_socket.accept()
# 5. 爲這個客戶端服務,這裏啓動多進程去處理服務器接收的請求
p = multiprocessing.Process(target=service_client, args=(new_socket,))
p.start() #同樣,子進程都是用start()方法啓動
new_socket.close() #注意使用多進程以後這裏多了一個close().之前單進程沒有的。主進程和子進程都要關閉。
# 關閉監聽套接字
tcp_server_socket.close()
if __name__ == "__main__":
main()
'''瀏覽器請求後服務器打印顯示的請求記錄
D:\software\python3\python.exe D:/pythoyworkspace/file_demo/Class_Demo/pachong/bbb.py
>>>>>>>>>>>>>>>>>>>>
['GET / HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'Upgrade-Insecure-Requests: 1', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 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', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /classic.css HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: text/css,*/*;q=0.1', 'Referer: http://192.168.1.1:7890/', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /images/qt-logo.png HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: image/webp,image/apng,image/*,*/*;q=0.8', 'Referer: http://192.168.1.1:7890/', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /images/mainwindow-vertical-tabs.png HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: image/webp,image/apng,image/*,*/*;q=0.8', 'Referer: http://192.168.1.1:7890/qt4-3-intro.html', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /images/mainwindow-custom-dock.png HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: image/webp,image/apng,image/*,*/*;q=0.8', 'Referer: http://192.168.1.1:7890/qt4-3-intro.html', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
...........
'''
使用瀏覽器去訪問服務器網址,瀏覽器成功解析,並且網頁中的子鏈接也可以打卡,成功處理,完成響應。
2.python實現多進程服務器,用類封裝程序
下面基於實際開發,將上面的程序使用類進行封裝,結果如下,實際訪問功能和上面一樣。
#coding=utf-8
import socket
import re
import multiprocessing
class WSGIServer(object):
def __init__(self, server_address):
# 創建一個tcp套接字
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 允許立即使用上次綁定的port
self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 綁定
self.listen_socket.bind(server_address)
# 變爲被動,並制定隊列的長度
self.listen_socket.listen(128)
def serve_forever(self):
"循環運行web服務器,等待客戶端的鏈接併爲客戶端服務"
while True:
# 等待新客戶端到來
client_socket, client_address = self.listen_socket.accept()
print(client_address) # for test
new_process = multiprocessing.Process(target=self.handleRequest, args=(client_socket,))
new_process.start()
# 因爲子進程已經複製了父進程的套接字等資源,所以父進程調用close不會將他們對應的這個鏈接關閉的
client_socket.close()
def handleRequest(self, client_socket):
"用一個新的進程,爲一個客戶端進行服務"
recv_data = client_socket.recv(1024).decode('utf-8')
print(recv_data)
requestHeaderLines = recv_data.splitlines()
for line in requestHeaderLines:
print(line)
request_line = requestHeaderLines[0]
get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)
print("file name is ===>%s" % get_file_name) # for test
if get_file_name == "/":
get_file_name = DOCUMENTS_ROOT + "/index.html"
else:
get_file_name = DOCUMENTS_ROOT + get_file_name
print("file name is ===2>%s" % get_file_name) # for test
try:
f = open(get_file_name, "rb")
except IOError:
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "\r\n"
response_body = "====sorry ,file not found===="
else:
response_header = "HTTP/1.1 200 OK\r\n"
response_header += "\r\n"
response_body = f.read()
f.close()
finally:
client_socket.send(response_header.encode('utf-8'))
client_socket.send(response_body)
client_socket.close()
# 設定服務器的端口
SERVER_ADDR = (HOST, PORT) = "", 8888
# 設置服務器服務靜態資源時的路徑
DOCUMENTS_ROOT = "./html"
def main():
httpd = WSGIServer(SERVER_ADDR)
print("web Server: Serving HTTP on port %d ...\n" % PORT)
httpd.serve_forever()
if __name__ == "__main__":
main()
3.python通過多線程的方式實現http服務器
多進程改成多線程代碼上改動的很少,只是將multiprocessing改成threading即可,如下:
#coding=utf-8
import socket
import re
import threading
class WSGIServer(object):
def __init__(self, server_address):
# 創建一個tcp套接字
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 允許立即使用上次綁定的port
self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 綁定
self.listen_socket.bind(server_address)
# 變爲被動,並制定隊列的長度
self.listen_socket.listen(128)
def serve_forever(self):
"循環運行web服務器,等待客戶端的鏈接併爲客戶端服務"
while True:
# 等待新客戶端到來
client_socket, client_address = self.listen_socket.accept()
print(client_address)
new_process = threading.Thread(target=self.handleRequest, args=(client_socket,))
new_process.start()
# 因爲線程是共享同一個套接字,所以主線程不能關閉,否則子線程就不能再使用這個套接字了
# client_socket.close()
def handleRequest(self, client_socket):
"用一個新的進程,爲一個客戶端進行服務"
recv_data = client_socket.recv(1024).decode('utf-8')
print(recv_data)
requestHeaderLines = recv_data.splitlines()
for line in requestHeaderLines:
print(line)
request_line = requestHeaderLines[0]
get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)
print("file name is ===>%s" % get_file_name) # for test
if get_file_name == "/":
get_file_name = DOCUMENTS_ROOT + "/index.html"
else:
get_file_name = DOCUMENTS_ROOT + get_file_name
print("file name is ===2>%s" % get_file_name) # for test
try:
f = open(get_file_name, "rb")
except IOError:
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "\r\n"
response_body = "====sorry ,file not found===="
else:
response_header = "HTTP/1.1 200 OK\r\n"
response_header += "\r\n"
response_body = f.read()
f.close()
finally:
client_socket.send(response_header.encode('utf-8'))
client_socket.send(response_body)
client_socket.close()
# 設定服務器的端口
SERVER_ADDR = (HOST, PORT) = "", 8888
# 設置服務器服務靜態資源時的路徑
DOCUMENTS_ROOT = "./html"
def main():
httpd = WSGIServer(SERVER_ADDR)
print("web Server: Serving HTTP on port %d ...\n" % PORT)
httpd.serve_forever()
if __name__ == "__main__":
main()
同樣使用瀏覽器可以訪問這個多線程服務器,功能上來說,也是差不多。多線程同時處理請求。
統一聲明:關於原創博客內容,可能會有部分內容參考自互聯網,如有原創鏈接會聲明引用;如找不到原創鏈接,在此聲明如有侵權請聯繫刪除哈。關於轉載博客,如有原創鏈接會聲明;如找不到原創鏈接,在此聲明如有侵權請聯繫刪除哈