多進程一般來說要比單進程效率高,因爲多進程可以解決了單進程recv()阻塞等待的問題。而實際上單進程也可以有非阻塞模式,實現多進程的功能,並且效率更高。所謂單線程非阻塞模式:
1.首先開啓socket非阻塞模式
2.然後將socket接受的新客戶端請求放入到一個列表中,不用等待其接受數據完成
3.遍歷列表,實現數據接收,如果已經接收或則客戶端斷開連接,則將該新客戶端請求移除列表。
1.python實現單進程非堵塞服務器模型
import socket
import time
tcp_server_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_tcp.bind(("", 7899))
tcp_server_tcp.listen(128)
tcp_server_tcp.setblocking(False) # 設置套接字爲非堵塞的方式
client_socket_list = list()
while True:
time.sleep(0.5)
try:
new_socket, new_addr = tcp_server_tcp.accept()
except Exception as ret:
print("---沒有新的客戶端到來---")
else:
print("---只要沒有產生異常,那麼也就意味着 來了一個新的客戶端----")
new_socket.setblocking(False) # 設置套接字爲非堵塞的方式
client_socket_list.append(new_socket)
for client_socket in client_socket_list:
try:
recv_data = client_socket.recv(1024)
except Exception as ret:
print(ret)
print("----這個客戶端沒有發送過來數據----")
else:
print("-----沒有異常-----")
print(recv_data)
if recv_data:
# 對方發送過來數據
print("----客戶端發送過來了數據-----")
else:
# 對方調用close 導致了 recv返回
client_socket.close()
client_socket_list.remove(client_socket)
print("---客戶端已經關閉----")
如下,通過網絡調試助手,實現多客戶端與之同時通信,服務器以單線程非阻塞模式運行。1.先啓動一個網絡調試助手1,與服務器進行連接,然後不發送數據。2.再啓動一個網絡調試助手2,建立連接,這個時候發送數據。結果顯示服務器端非阻塞模式,與調試助手2進行通信,而不是等到調試助手1發送數據過來。兩者可以併發運行。
2.python實現單進程非堵塞服務器並且與瀏覽器進行通信
以上面單線程非阻塞模型爲基礎,基於之前案例實現一個單線程非阻塞的HTTP服務器,實現瀏覽器可以基於http協議進行發送請求和解析。瀏覽器展示返回的一個標準的HTML網頁,此外實現服務器解析客戶端多次請求並且返回請求結果。即:客戶端根據HTML裏面的各種鏈接,再發送HTTP請求給服務器,拿到相應的圖片、視頻、Flash、JavaScript腳本、CSS等各種資源,最終顯示出一個完整的頁面。
import time
import socket
import re
class WSGIServer(object):
"""定義一個WSGI服務器的類"""
def __init__(self, port, documents_root):
# 1. 創建套接字
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 綁定本地信息
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind(("", port))
# 3. 變爲監聽套接字
self.server_socket.listen(128)
self.server_socket.setblocking(False)
self.client_socket_list = list()
self.documents_root = documents_root
def run_forever(self):
"""運行服務器"""
# 等待對方鏈接
while True:
# time.sleep(0.5) # for test
try:
new_socket, new_addr = self.server_socket.accept()
except Exception as ret:
print("-----1----", ret) # for test
else:
new_socket.setblocking(False)
self.client_socket_list.append(new_socket)
for client_socket in self.client_socket_list:
try:
request = client_socket.recv(1024).decode('utf-8')
except Exception as ret:
print("------2----", ret) # for test
else:
if request:
self.deal_with_request(request, client_socket) #將接受到的請求組發給deal_with_request函數去完成解析和響應
else:
client_socket.close()
self.client_socket_list.remove(client_socket)
print(self.client_socket_list)
def deal_with_request(self, request, client_socket):
"""爲這個瀏覽器服務器"""
if not request:
return
request_lines = request.splitlines()
for i, line in enumerate(request_lines):
print(i, line)
# 提取請求的文件(index.html)
# GET /a/b/c/d/e/index.html HTTP/1.1
ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])
if ret:
print("正則提取數據:", ret.group(1))
print("正則提取數據:", ret.group(2))
file_name = ret.group(2)
if file_name == "/":
file_name = "/index.html"
# 讀取文件數據
try:
f = open(self.documents_root+file_name, "rb")
except:
response_body = "file not found, 請輸入正確的url"
response_header = "HTTP/1.1 404 not found\r\n"
response_header += "Content-Type: text/html; charset=utf-8\r\n"
response_header += "Content-Length: %d\r\n" % (len(response_body))
response_header += "\r\n"
# 將header返回給瀏覽器
client_socket.send(response_header.encode('utf-8'))
# 將body返回給瀏覽器
client_socket.send(response_body.encode("utf-8"))
else:
content = f.read()
f.close()
response_body = content
response_header = "HTTP/1.1 200 OK\r\n"
response_header += "Content-Length: %d\r\n" % (len(response_body))
response_header += "\r\n"
# 將header返回給瀏覽器
client_socket.send( response_header.encode('utf-8') + response_body)
# 設置服務器服務靜態資源時的路徑
DOCUMENTS_ROOT = "./html"
def main():
port = 7777
http_server = WSGIServer(port, DOCUMENTS_ROOT)
http_server.run_forever()
if __name__ == "__main__":
main()
使用瀏覽器連接該服務器地址,可以實現正常網頁的訪問和響應。
統一聲明:關於原創博客內容,可能會有部分內容參考自互聯網,如有原創鏈接會聲明引用;如找不到原創鏈接,在此聲明如有侵權請聯繫刪除哈。關於轉載博客,如有原創鏈接會聲明;如找不到原創鏈接,在此聲明如有侵權請聯繫刪除哈