服務器
硬件服務器
計算機主機
軟件服務器
- 網絡服務器,提供后端邏輯服務和請求處理的程序集合及架構
- 編寫的服務端應用程序,在硬件服務器上運行昔期,一般依托于操作系統(tǒng)梦抢,給用戶提供一套完整的服務
服務器架構
服務器的組織形式
c/s 客戶端服務器模型
b/s 瀏覽器服務器模型
服務器目標
- 處理速度更快,并發(fā)量更高,安全性更強
- 硬件:更高的配置萤厅,更好的集成分布技術玉锌,更好的網絡優(yōu) 化和網絡安全技術
- 軟件:占用資源更少,運行更穩(wěn)定救湖,算法更優(yōu)良邑闺,安全性 更好,并發(fā)性更高蹭沛,更容易擴展
常見服務器
- httpserver:處理http請求
- webserver:網站的后端應用服務器程序
- 郵箱服務器:郵件處理
- ftp文件服務器:文件的上傳下載
功能:網絡連接,邏輯處理,數(shù)據交互眷蜈,數(shù)據傳輸忌怎,協(xié)議的實現(xiàn)
服務器模型
循環(huán)服務器模型
- 循環(huán)接收客戶端請求晚岭,處理請求辅甥。同一時刻只能處理一個請求构回,處理完畢后再處理下一個
- 實現(xiàn)簡單,占用資源少
- 不能同時處理多個客戶端請求,不允許某個客戶端長期占用服務器資源
- 由于UDP不需要進行連接,所以循環(huán)服務器模型更加適合UDP通信
并發(fā)服務器模型
能夠同時處理多個客戶端請求,每有一個客戶端就創(chuàng)建一個進程或線程處理客戶端的具體請求事件歇由,而主進程或主線程繼續(xù)接受其他客戶端的連接
IO并發(fā)(IO多路復用)
- 優(yōu)點:資源消耗少,IO處理速度快
- 缺點:不能適用cpu密集型程序
多進程/多線程并發(fā)
- 為每個客戶端創(chuàng)建單獨的進程線程,執(zhí)行請求
- 優(yōu)點:每個客戶端可以長期占有服務器運行程序,能夠使用多核資源,可以處理IO或者cpu運算
- 缺點:消耗系統(tǒng)資源高
多進程并發(fā)服務器模型
使用fork實現(xiàn)多進程并發(fā)
- 創(chuàng)建套接字,綁定,監(jiān)聽
- 主進程等待接收客戶端請求
- 創(chuàng)建新的子進程處理客戶端請求
- 原有主進程繼續(xù)等待接收新的客戶端連接
- 如果客戶端退出則關閉子進程
# fork_server.py
from socket import *
import sys
import signal
import os
# 主機地址與端口
HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
# 主進程忽略子進程的退出,子進程退出自動由系統(tǒng)處理
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
# 客戶端處理函數(shù)
def client_handler(c):
print('服務器已連接到客戶端:',c.getpeername())
try:
while True:
data = c.recv(1024)
if not data:
break
print('%s說:%s'%(c.getpeername(),data.decode()))
c.send('收到客戶端請求'.encode())
except (KeyboardInterrupt,SystemError):
sys.exit('客戶端退出')
except Exception as e:
print('服務器內部錯誤信息:',e)
c.close()
sys.exit(0)
# 創(chuàng)建套接字
s = socket(AF_INET,SOCK_STREAM,0)
# 設置端口可重用
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
# 綁定
s.bind(ADDR)
# 監(jiān)聽
s.listen(5)
# 主進程等待接收客戶端請求
print('進程%d正在等待接受請求:'%os.getpid())
while True:
try:
c,addr = s.accept()
except KeyboardInterrupt:
sys.exit('服務器退出,暫無服務可連')
except Exception as e:
print('錯誤信息:',e)
continue
# 創(chuàng)建新的子進程處理客戶端請求
pid = os.fork()
# 判斷是否是子進程
if pid == 0:
s.close()
client_handler(c)
# 如果不是子進程或者創(chuàng)建失敗,則繼續(xù)等待接受其他客戶端
else:
c.close()
continue
使用multiprocessing模塊實現(xiàn)多進程并發(fā)
from socket import *
import os,sys
from multiprocessing import Process
import traceback
def handler():
print("Connect from",c.getpeername())
while True:
data = c.recv(1024).decode()
if not data:
break
print(data)
c.send(b'Receive request')
c.close()
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8899))
s.listen(5)
while True:
try:
c,addr = s.accept()
except KeyboardInterrupt:
s.close()
sys.exit("服務器退出")
except Exception:
traceback.print_exc()
continue
t = Process(target = handler)
t.daemon = True
t.start()
多線程并發(fā)服務器模型
對比多進程并發(fā)
- 消耗資源較少
- 線程應該更注意共享資源的操作
- 在python中應該注意GIL問題,網絡延遲較高,線程并發(fā)也是一種可行的辦法
實現(xiàn)步驟
- 創(chuàng)建套接字夜只,綁定監(jiān)聽
- 接收客戶端請求,創(chuàng)建新的線程
- 主線程繼續(xù)接收其他客戶端連接
- 分支線程啟動對應的函數(shù)處理客戶端請求
- 當客戶端斷開,則分支線程結束
from socket import *
import os,sys
from threading import *
import traceback
def handler(c):
print("Connect from",c.getpeername())
while True:
data = c.recv(1024).decode()
if not data:
break
print(data)
c.send(b'Receive request')
c.close()
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8899))
s.listen(5)
while True:
try:
c,addr = s.accept()
except KeyboardInterrupt:
s.close()
sys.exit("服務器退出")
except Exception:
traceback.print_exc()
continue
t = Thread(target = handler,args = (c,))
t.daemon = True
t.start()
補充
import os
# 獲取目錄中文件列表
os.listdir(path)
# 判斷是否為普通文件
os.path.isfile()
# 判斷是否為目錄
os.path.isdir()
import traceback
# 更詳細的打印異常信息
traceback.print_exc()
集成模塊socketserver
通過模塊的不同類的組合完成多進程/多線程的TCP/UDP的并發(fā)
套接字請求
StreamRequestHandler
處理tcp套接字請求
DatagramRequestHandler
處理udp套接字請求
創(chuàng)建套接字
TCPServer
創(chuàng)建tcp server
UDPServer
創(chuàng)建udp server
創(chuàng)建多進程
ForkingMixIn
ForkingTCPServer <--> ForkingMinIn + TCPServer
ForkingUDPServer <--> ForkingMinIn + UDPServer
創(chuàng)建多線程
ThreadingMixIn
ThreadingTCPServer <--> ThreadingMinIn + TCPServer
ThreadingUDPServer <--> ThreadingMinIn + UDPServer
步驟
- 創(chuàng)建服務器類
- 創(chuàng)建處理類
- 使用創(chuàng)建的服務器類來產生服務器