socket服務端實現併發、進程池/線程池、協程、IO模型

一.soccket服務端實現併發

​ 網絡編程服務端要知足一下三點要求: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.實現高併發:將單線程的效率提高到最高,多進程先開多線程,多線程下使用協程

3.1 gevent模塊

​ 一個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)  # 單線程下實現併發,提高效率

3.2 運用協程實現服務端客戶端通訊

連接和通訊都是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

  • 非阻塞IO(服務端通訊針對accept用s.setblocking(False)加異常捕獲,cpu佔用率太高)

  • IO多路複用

    在只檢測一個套接字的狀況下,他的效率連阻塞IO都比不上。由於select這個中間人增長了環節。

    可是在檢測多個套接字的狀況下,就能省去wait for data過程

  • 異步IO

相關文章
相關標籤/搜索