day 32

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
相關文章
相關標籤/搜索