網絡編程服務端要知足一下三點要求:python
- 1. 固定的ip和port程序員
- 2. 24小時不間斷提供服務編程
- 3. 可以實現併發安全
#服務端 import socket from threading import Thread """ 服務端: 1.固定的ip和port 2.24小時不間斷提供服務 3.支持高併發 """ server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) # 半鏈接池 def communicate(conn): while True: try: data = conn.recv(1024) # 阻塞 if len(data) == 0:break print(data) conn.send(data.upper()) except ConnectionResetError: break conn.close() while True: conn,addr = server.accept() # 阻塞 print(addr) t = Thread(target=communicate,args=(conn,)) t.start() # 客戶端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: info = input('>>>:').encode('utf-8') if len(info) == 0:continue client.send(info) data = client.recv(1024) print(data)
- 不管開線程仍是開進程其實都消耗資源,開線程消耗的資源比開進程小服務器
- 池: 爲了減緩計算機硬件的壓力,避免計算機硬件設備奔潰網絡
雖然減輕了計算機硬件的壓力,可是必定程度上下降了持續的效率多線程
- 進程池/線程池:併發
爲了限制開設的進程數和線程數,從而保證計算機硬件的安全app
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor import time import os # 示例化池對象 # 不知道參數的狀況,默認是當前計算機cpu個數乘以5,也能夠指定線程個數 pool = ProcessPoolExecutor(5) # 建立了一個池子,池子裏面有20個線程 def task(n): print(n, os.getpid()) time.sleep(2) return n ** 2 def call_back(n): print('我拿到告終果:%s' % n.result()) """ 提交任務的方式 同步:提交任務以後,原地等待任務的返回結果,再繼續執行下一步代碼 異步:提交任務以後,不等待任務的返回結果(經過回調函數拿到返回結果並處理),直接執行下一步操做 """ # 回調函數:異步提交以後一旦任務有返回結果,自動交給另一個去執行 if __name__ == '__main__': # pool.submit(task,1) t_list = [] for i in range(20): future = pool.submit(task, i).add_done_callback(call_back) # 異步提交任務 t_list.append(future) # pool.shutdown() # 關閉池子而且等待池子中全部的任務運行完畢 # for p in t_list: # print('>>>:',p.result()) print('主')
- 進程:資源單位(車間)異步
- 線程:最小執行單位(流水線)
- 協程:單線程下實現併發
1.協程徹底是程序員本身想出來的的東西,經過代碼層面本身檢測io本身實現切換,
讓操做系統誤認爲這個線程沒有io
2.切換+保存狀態必定能夠提高程序效率嗎?
按狀況考慮:1.當任務是計算密集型的任務時,反而會下降效率
2.當任務是IO密集型任務是,能夠提升運行效率
3.實現高併發:將單線程的效率提高到最高,多進程先開多線程,多線程下使用協程
一個spawn就是一個管理任務的對象
gevent模塊不能識別它自己以外的IO行爲
因此必須導入它內部封裝的模塊,能夠幫助咱們識別全部io行爲
from gevent import monkey;monkey.patch_all() # 檢測全部的IO行爲 from gevent import spawn,joinall # joinall列表裏面放多個對象,實現join效果 import time def play(name): print('%s play 1' %name) time.sleep(5) print('%s play 2' %name) def eat(name): print('%s eat 1' %name) time.sleep(3) print('%s eat 2' %name) start=time.time() g1=spawn(play,'劉清正') g2=spawn(eat,'劉清正') # g1.join() # g2.join() joinall([g1,g2]) print('主',time.time()-start) # 單線程下實現併發,提高效率
連接和通訊都是io密集型操做,咱們要在這二者之間來回切換,就能實現併發效果
服務端檢測鏈接和通訊任務,客戶端起多線程同時連接服務器
# 服務端 from gevent import monkey;monkey.patch_all() from socket import * from gevent import spawn def communicate(conn): while True: try: data = conn.recv(1024) if len(data) == 0: break conn.send(data.upper()) except ConnectionResetError: break conn.close() def server(ip, port, backlog=5): server = socket(AF_INET, SOCK_STREAM) server.bind((ip, port)) server.listen(backlog) while True: # 連接循環 conn, client_addr = server.accept() print(client_addr) # 通訊 spawn(comunicate,conn) if __name__ == '__main__': g1=spawn(server,'127.0.0.1',8080) g1.join() # 客戶端 from threading import Thread, current_thread from socket import * def client(): client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8080)) n = 0 while True: msg = '%s say hello %s' % (current_thread().name, n) n += 1 client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8')) if __name__ == '__main__': for i in range(500): t = Thread(target=client) t.start() # 本來服務端須要開啓500個線程才能跟500個客戶端通訊,如今只須要一個線程就能夠扛住500客戶端 # 進程下面開多個線程,線程下面再開多個協程,最大化提高軟件運行效率
阻塞IO
非阻塞IO(服務端通訊針對accept用s.setblocking(False)加異常捕獲,cpu佔用率太高)
IO多路複用
在只檢測一個套接字的狀況下,他的效率連阻塞IO都比不上。由於select這個中間人增長了環節。
可是在檢測多個套接字的狀況下,就能省去wait for data過程
異步IO