day 32
# 軟件開發架構:
C/S:
Client: 客戶端
Server: 服務端
優勢:
佔用網絡資源少,軟件的使用穩定
缺點:
服務端更新後,客戶端也得跟着跟新.
須要使用多個軟件,須要下載多個客戶端
B/S:
Browser: 瀏覽器(客戶端)
Server: 服務端
服務端與客戶端做用:
服務端: 24小時不間斷提供服務
客戶端: 須要體驗服務端時,再去鏈接服務端,並享受服務
一 網絡編程:
1.互聯網協議OSI七層協議
1)應用層
2)表示層
3)會話層
4)傳輸層
5)網絡層
6)數據鏈路層
7)物理鏈接層
- 物理鏈接層
基於電信號發送二進制數據.
- 數據鏈路層
1) 規定好電信號的分組方式
2) 必需要有一塊網卡:
- mac地址:
12位惟一的16進制字符串
- 前6位: 廠商號
- 後6位: 流水號
- 以太網協議:
在同一個局域網內通訊.
- 單播
1對1吼
- 廣播
多對多吼
- 廣播風暴:
- 不能跨局域網通訊
- 網絡層
- ip: 定位局域網的位置
- port: 惟一標識一臺計算機上一個應用程序.
- arp協議:
將mac地址獲取,並解析成ip和port.
- 傳輸層
- TCP
特色:
TCP協議稱之爲流式協議.
若想要通訊,必須創建鏈接,並創建雙向通道.
- 三次握手,四次揮手
- 三次握手建鏈接
- 客戶端往服務端發送請求創建通道
- 服務端要確認客戶端的請求,並往客戶端也發送請求創建通道
- 客戶端接收到服務端創建鏈接的請求,並返回確認
- 創建雙向通道
- 雙向通道:
- 反饋機制
客戶端往服務端發送請求獲取數據,服務端務必返回數據,客戶端確認收到.
反則會反覆發送,一直到某個時間段內,會中止發送
- 四次揮手斷鏈接
- C往S發送斷開鏈接請求,S返回確認收到
- S須要再次發送斷開鏈接請求
- C返回確認收到
- 最終確認斷開鏈接
- UDP
1)數據不安全
2)不須要創建雙向通道
3)傳輸速度快
4)不會有粘包問題
5)客戶端發送數據,不須要服務端確認收到,愛收不收
TCP與UPD的區別:
TCP: 比喻成在打電話
UDP: 比喻成發送短信
- 應用層
- ftp
- http:
能夠攜帶一堆數據
- http + ssl
2.socket
socket用來寫套接字客戶端與服務端的模塊,內部幫咱們封裝好了7層協議須要作的事情.
3.手擼socket套接字模板
- 服務端:
import socket
server = socket.socket()
server.bind(
(ip, port)
) # 綁定手機號
server.listen(6) # 半鏈接池: 能夠接待7個客戶端
# 監聽鏈接
conn, addr =server.accept()
# 接收消息
data = conn.recv(1024)
# 發送消息
conn.send('消息內容'.encode('utf-8'))
- 客戶端:
import socket
client = socket.socket()
client.connect(
(ip, port)
)
# 發送消息
client.send()
# 接收消息
client.recv(1024)
4.subprocess(瞭解)
用來經過代碼往cmd建立一個管道,而且發送命令和接收cmd返回的結果.
import subprocess
obj = subprocess.Popen(
'cmd命令',
shell=True,
# 接收正確結果
stdout=subprocess.PIPE,
# 接收錯誤結果
stderr=subprocess.PIPE
)
success = obj.stdout.read()
error = obj.stderr.read()
msg = success + error
5.黏包問題
1.不能肯定對方發送數據的大小
2.在短期內,間隔時間短,而且數據量小的狀況, 默認將這些數據打包成一個
屢次發送的數據 ---> 一次性發送
6.struct解決黏包問題
初級版:
i: 4
能夠將一個數據的長度打包成一個固定長度的報頭.
struct.pack('模式i', '源數據長度')
data = 'gagawagwaga'
# 打包成報頭
headers = struct.pack('i', len(data))
# 解包獲取數據真實長度
data = struct.unpack('i', headers)[0]
注意: 以什麼方式打包,必須以什麼方式解包.
升級版:
先將數據存放到字典中,將字典打包發送過去
- 字典的好處:
- 真實數據長度
- 文件的描述信息
- 發送的數據,更小
dic = {
'data_len': 1000000000000000000000046546544444444444444444444444444444444444444,
文件的描述信息
}
7.上傳大文件數據
# 客戶端
dic = {
文件大小,
文件名
}
with open(文件名, 'rb') as f:
for line in f:
client.send(line)
# 服務端
dic = {
文件大小,
文件名
}
init_recv = 0
with open(文件名, 'wb') as f:
while init_recv < 文件大小:
data = conn.recv(1024)
f.write(data)
init_recv += len(data)
10.socketserver(現階段,瞭解)
- 能夠支持併發
import socketserver
# 定義類
# TCP: 必須繼承BaseRequestHandler類
class MyTcpServer(socketserver.BaseRequestHandler):
- handle
# 內部實現了
server = socket.socket()
server.bind(
('127.0.0.1', 9527)
)
server.listen(5) ---
while True:
conn, addr = server.accept()
print(addr)
# 必須重寫父類的handle, 當客戶端鏈接時會調用該方法
def handle(self):
print(self.client_address)
while True:
try:
# 1.接收消息
# request.recv(1024) == conn.recv(1024)
data = self.request.recv(1024).decode('utf-8')
send_msg = data.upper()
self.request.send(send_msg.encode('utf-8'))
except Exception as e:
print(e)
break
TCP:
SOCK_STREAM
conn.recv()
UDP模板:
SOCK_DGRAM
server.recvfrom()
- 服務端
import socket
server = socket.socket(
type=socket.SOCK_DGRAM
)
server.bind(
(ip, port)
)
data, addr = server.recvfrom(1024)
server.sendto(data, addr)
- 客戶端
import socket
client = socket.socket(
type=socket.SOCK_DGRAM
)
ip_port = (ip, port)
client.sendto(data, ip_port)
data, _ = client.recvfrom(1024)
print(data)
二 併發編程
12.多道技術
- 單道
- 多道: 切換 + 保存狀態
- 空間上的複用
支持多個程序使用
- 時間上的複用
- 遇到IO操做就會切換程序
- 程序佔用CPU時間過長切換
13.併發與並行
併發: 看起來像同時運行: 多道技術
並行: 真正意義上的同時運行: 多核下
14.進程
進程是資源單位,沒建立一個進程都會生成一個名稱空間,佔用內存資源.
- 程序與進程
程序就是一堆代碼
進程就是一堆代碼運行的過程
- 進程調度
- 時間片輪轉法
10個進程, 將固定時間,等分紅10份時間片,分配給每個進程.
- 分級反饋隊列
1級別:
2級別:
3級別:
- 進程的三個狀態
- 就緒態:
建立多個進程, 必需要排隊準備運行
- 運行態:
進程開始運行, 1.結束 2.阻塞
- 阻塞態:
當運行態遇到IO操做,就會進阻塞態.
- 同步與異步
提交任務的方式
- 同步: 同步提交, 串行,一個任務結束後,另外一個任務才能提交併執行.
- 異步: 異步提交, 多個任務能夠併發運行
- 阻塞與非阻塞
- 阻塞:
阻塞態
- 非阻塞:
就緒態
運行態
- 同步和異步、阻塞和非阻塞的區別。
二者是不一樣的概念,不能混爲一談.
- 建立進程的兩種方式
一:
p = Process(target=任務, args=(任務的參數, ))
p.daemon = True # 必須放在start()前,不然報錯
p.start() # 向操做系統提交建立進程的任務
p.join() # 向操做系統發送請求, 等全部子進程結束,父進程再結束
二:
class MyProcess(Process):
def run(self): # self == p
任務的過程
p = MyProcess()
p.daemon = True # 必須放在start()前,不然報錯
p.start() # 向操做系統提交建立進程的任務
p.join() # 向操做系統發送請求, 等全部子進程結束,父進程再結束
- 回收進程資源的兩種條件
- 調用join讓子結束後,主進程才能結束.
- 主進程正常結束
15.殭屍進程與孤兒進程(瞭解)
殭屍進程: 凡是子進程結束後,PID號還在, 主進程意外死亡,無法給子進程回收資源.
- 每一個子進程結束後,都會變成,殭屍進程 (PID)
孤兒進程: 凡是子進程沒有結束,可是主進程意外死亡.操做系統優化機制(孤兒院),
會將沒有主,而且存活的進程,在該進程結束後回收資源.
16.守護進程
只要父進程結束,全部的子進程都必須結束.
17.互斥鎖
將併發變成串行,犧牲執行效率,保證數據安全.
from multiprocessing import Lock
mutex = Lock()
# 加鎖
mutex.acquire()
修改數據
mutex.release()
18.隊列
- FIFO隊列: 先進先出
from multiprocessing import Queue
q = Queue(5)
# 添加數據,若隊列添加數據滿了,則等待
q.put()
# 添加數據,若隊列添加數據滿了,直接報錯
q.put_nowait()
# 獲取隊列中的數據
q.get() # 若隊列中沒數據,會卡住等待
q.get_nowait() # 若隊列中沒數據,會直接報錯
19.堆棧
LIFO
20.IPC進程間通訊
- 進程間的數據是隔離的
- 隊列可讓進程間通訊
- 把一個程序放入隊列中,另外一個程序從隊列中獲取,實現進程間數據交互
21.生產者與消費者 模型
生產者: 生產數據
消費者: 使用數據
爲了保證 供需平衡.
經過隊列實現, 生產者將數據扔進隊列中,消費者從隊列中獲取數據.
能夠保證一邊生產一邊消費.
22.線程
- 什麼是線程
- 進程: 資源單位
- 線程: 執行單位
- 建立進程時,會自帶一個線程
一個進程下能夠建立多個線程.
- 使用線程的好處
節省資源的開銷
- 進程與線程優缺點:
- 進程:
優勢:
- 多核下能夠並行執行
- 計算密集型下提升效率
缺點:
- 開銷資源遠高於線程
- 線程:
優勢:
- 佔用資源遠比進程小
- IO密集型下提升效率
缺點:
- 沒法利用多核優點
23.線程間數據是共享的
- 畫圖
24.GIL全局解釋器鎖
- 只有Cpython纔有自帶一個GIL全局解釋器鎖
1.GIL本質上是一個互斥鎖.
2.GIL的爲了阻止同一個進程內多個線程同時執行(並行)
- 單個進程下的多個線程沒法實現並行,但能實現併發
3.這把鎖主要是由於CPython的內存管理不是 "線程安全" 的.
- 內存管理
- 垃圾回收機制
注意: 多個線程過來執行,一旦遇到IO操做,就會立馬釋放GIL解釋器鎖,交給下一個先進來的線程.
總結: GIL的存在就是爲了保證線程安全的,保證數據安全
25.多線程使用的好處
- 多線程:
IO密集型,提升效率
- 多進程
計算密集型,提升效率
26.死鎖現象(瞭解)
27.遞歸鎖(瞭解,之後不用)
解決死鎖現象
mutex = Lock() # 只能引用1次
mutex1, mutex2 = RLock() # 能夠引用屢次
+1, 只要這把鎖計數爲0釋放該鎖, 讓下一我的使用, 就不會出現死鎖現象.
28.信號量(絕對了解)
信號量也是一把鎖, 可讓多個任務一塊兒使用.
互斥鎖:
只能讓一個任務使用
信號量:
可讓多個任務一塊兒使用.
sm = Semaphore(5) 可讓5個任務使用
29.線程隊列
使用場景:
若線程間數據不安全狀況下使用線程隊列, 爲了保證線程間數據的安全.
import queue
- FIFO: 先進先出隊列
queue.Queue()
- LIFO: 後進先出隊列
queue.LifoQueue()
- 優先級隊列:
- 根據數字大小判斷,判斷出隊優先級.
- 進隊數據是無序的
queue.PriorityQueue()
30.event事件
能夠控制線程的執行,讓一些線程控制另外一些線程的執行.
e = Event()
- 線程1
e.set() # 給線程2發送信號,讓他執行
- 線程2
e.wait() # 等待線程1的信號
31.進程池與線程池
爲了控制進程/線程建立的數量,保證了硬件能正常運行.
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
pool1 = ProcessPoolExecutor() # 默認CPU個數
pool2 = ThreadPoolExecutor() # CPU個數 * 5
pool3 = ProcessPoolExecutor(100) # 100個
pool4 = ThreadPoolExecutor(200) # 200個
# 將函數地址的執行結果,給回調函數
pool4.submit(函數地址, 參數).add_done_callback(回調函數地址)
- 回調函數(必須接收一個參數res):
# 獲取值
res2 = res.result()
32.協程
- 進程: 資源單位
- 線程: 執行單位
- 協程: 單線程下實現併發, 不是任何的單位,是程序員YY出來的名字.
- 單線程下實現併發
好處是節省資源, 單線程 < 多線程 < 多進程
- IO密集型下:
協程有優點
- 計算密集型下:
進程有優點
- 高併發:
- 多進程 + 多線程 + 協程 (Nginx)
協程的建立:
手動實現切換 + 保存狀態:
- yield
- 函數一直在調用next()
會不停地切換
yield不能監聽IO操做的任務
- gevent來實現監聽IO操做
33.gevent
pip3 install gevent
from gevent import monkey
monkey.patch_all() # 設置監聽全部IO
from gevent import spawn, joinall # 實現 切換 + 保存狀態
- 實現了單線程下實現併發
s1 = spawn(任務1)
s2 = spawn(任務2)
joinall([s1, s2])
34.IO模型(瞭解)
- 阻塞IO
- 非阻塞IO
- 多路複用IO
- 異步IO