服務端主動給客戶端推送消息


服務端主動給客戶端推送消息

如何實現(目錄)
  • 輪詢
  • 長輪詢
  • websocket

應用場景:大屏幕實時投票、任務執行的流程...

飯前甜點

  • ajax操作

    異步提交,局部刷新。用它就可以實現我們上面的輪詢/長輪詢

    $.ajax({
      url:'',  # 后端服務器地址
      type:'',  # 請求方式
      data:{},  # 發送的數據
      dataType:'JSON',  # 如果django后端是通過HttpResponse返回數據的那么不會自動返序列化,而如果是JsonResponse則會自動轉化,該參數可以不指定
      success:function(args){
        # 執行成功之后需要進行的操作  異步回調
      }
    })
    
    return HttpResponse(json.dumps(back_dic))  # 需要dataType參數
    return JsonResponse(back_dic)  # 不需要
    

    補充:templates模版文件夾可以放在全局,也可以在每一個應用下創建該文件夾

    如果全局沒有,那么在查找模版的時候就會去應用下查找模版,順序是按照配置文件中注冊了的app的順序依次查找

  • 隊列

    隊列:先進先出

    堆棧:先進后出

    import queue
    
    
    q = queue.Queue()  # 創建一個隊列
    
    
    q.put('jason')  # 朝隊列中丟數據
    q.put('egon')
    
    
    v1 = q.get()  # 獲取數據
    v2 = q.get()
    # v3 = q.get()  # 如果沒有數據了,get方法默認會一直阻塞
    try:
        v3 = q.get(timeout=3)  # 等3秒 之后還沒數據直接報錯   queue.Empty
    except queue.Empty as e:
        print(e)
    print(v1,v2)
    # 注意該隊列並不會用於實際生產 主要是用來做本地的測試
    # 實際生產建議使用redis、kafka、rebittMQ
    

    基於隊列和ajax我們來設想:實現群聊功能

    pending狀態

  • 遞歸

    # python最大遞歸深度多少呀 997、998、... 官網說是1000
    def func():
      	func()
    func()
    
    # 在js中 根本沒有遞歸的概念 函數內部自己調用自己是可以的 屬於正常事件范疇
    function func1(){
      $.ajax({
        url:'',
        type:'get',
        data:{},
        success:function(args){
          func1()
        }
      }) 
    }
    $(function(){  # 等待頁面加載完畢之后執行函數內的代碼
     	func1()
    })
    
  • modelform

    forms組件的加強版

    自動校驗數據、渲染標簽、展示錯誤信息

服務端朝客戶端主動推送消息

  • 輪詢

    效率低、基本不用

    讓瀏覽器定時朝后端發送請求(通過ajax向后端偷偷發送數據),比如每隔五秒鍾發一次請求,那么你的數據延遲就可能會高達五秒
    
    不足之處
    	數據延遲
      消耗資源過大
      請求次數太多
    
  • 長輪詢

    兼容性好

    一般大公司都會考慮使用它

    # 隊列+ajax
    服務端給每個客戶端建立隊列,讓瀏覽器通過ajax朝服務端要數據,去各自的隊列中獲取
    如果沒有數據則會阻塞但是不會一直阻塞,比如阻塞你30秒,還沒有數據則返回,然后讓客戶端瀏覽器再次發送請求數據的請求
    
    相對於輪詢
    	基本是沒有消息延遲的
      請求次數降低了很多
      
    # web版本的qq和微信基本上用的都是這么一個邏輯
    

    基於ajax及隊列實現長輪詢的功能

    """
    1.首頁自定義用戶唯一表示,給每個用戶初始化一個隊列
    2.發送按鈕綁定點擊事件 后端講數據放入每一個隊列中
    3.書寫自動獲取數據的ajax代碼 循環調用
    4.前端獲取數據DOM操作渲染頁面
    """
    $('#d1').click(function () {
            $.ajax({
                url:'/send_msg/',
                type:'post',
                data:{'content':$('#d2').val()},
                dataType:'JSON',
                success:function (args) {
                }
            })
        });
        function getMsg(){
            $.ajax({
                url:'/get_msg/',
                type:'get',
                data:{'name':'{{ name }}'},  // 只要當前登陸人的隊列中的數據
                {#dataType:'JSON',#}
                success:function (args) {
                    // 針對返回的消息做相應的處理
                    if(args.status){
                        // 有消息則渲染頁面  講消息全局放到聊天紀錄里面
                        // 1 創建標簽
                        var pEle = $('<p>');
                        // 2 給標簽設置文本內容
                        pEle.text(args.msg);
                        // 3 講創建好的標簽添加到聊天記錄div標簽內
                        $('#content').append(pEle)
                    }else{
                        // 沒有消息 則繼續發送
                    }
                    getMsg()  // 循環請求數據
                }
            })
        }
        $(function () {
            getMsg()  // 等待頁面加載完畢自動執行
        })
              
              
    # 后端
    # 全局大字典
    q_dict = {}  # {'唯一表示':隊列,....}
    
    
    def ab_bl(request):
        # 獲取我們自定義的客戶端唯一標識
        name = request.GET.get('name')
        # 給每一個客戶端創建一個隊列
        q_dict[name] = queue.Queue()
        return render(request,'ab_bl.html',locals())
    
    def send_msg(request):
        if request.method == 'POST':
            # 獲取用戶發送的消息
            content = request.POST.get('content')
            # 講該消息傳遞給所有的隊列
            for q in q_dict.values():
                q.put(content)
            return HttpResponse('OK')
    
    def get_msg(request):
        name = request.GET.get('name')
        # 拿到對應的隊列
        q = q_dict.get(name)
        # 講隊列中可能有的數據取出並返回給前端瀏覽器
    
        # 定義一個字典與ajax進行交互
        back_dic = {'status':True,'msg':''}
        try:
            data = q.get(timeout=10)  # 等10s 沒有則直接報錯
            back_dic['msg'] = data
        except queue.Empty as e:
            back_dic['status'] = False
        return JsonResponse(back_dic)
        # return HttpResponse(json.dumps(back_dic))
    
  • websocket

    真正的做到服務端發送消息而不再是被動的發送

    目前主流的瀏覽器都是支持websocket

    """
    HTTP協議  網絡協議(不加密傳輸)
    HTTPS協議 網絡協議(加密傳輸)
    	上面兩個協議都是短鏈接
    
    websocket網絡協議  (加密傳輸)
    	瀏覽器和服務端創建鏈接之后 默認不再斷開 
    	兩端都可以基於該鏈接收發消息
    	websocket的誕生能夠真正做到服務端發送消息而不再是被動的發送
    """
    

    websocket內部原理(了解 面試可以說關鍵點)

    """
    分成兩大部分
    	1.握手環節:驗證服務端是否支持websocket協議
    		先連接服務器
    		
    		瀏覽器產生一個隨機字符串 給服務端發送一份(請求頭) 自己留一份
    		Sec-WebSocket-Key: ePW8kp1XqLNWbJxE/Q38SA==
    		服務端和客戶端都對隨機字符串做下面的操作
    		
    		隨機字符串 + magic string拼接
    		然后再講拼接好的結果進行加密處理(sha1/base64)的到密文
    		
    		瀏覽器自動比對雙方產生的密文是否一致,如果一致說明服務端支持websocket
    		如果不一致會報錯
    		
    		假設比對上了 建立websocket鏈接 基於該鏈接收發消息
    		
    	2.收發數據
    		密文傳輸 >>> 必然要涉及解密(全球統一)的過程
    		基於網絡傳輸的數據都是二進制格式 對應到我們python中就是bytes類型
    		
    		詳見qq群里面的截圖
    		數據解密過程
    			1.先讀取數據的第二個字節的后7位(payload) 
    			根據7位數據的大小來指定不同的解密流程
    				=127:再往后讀取8個字節
    				=126:再往后讀取2個字節
    				<=125:不再往后讀取
    				
    			除去前面讀取的數據之外 再往后讀4個字節(masking-key)
    			拿着它去解析后面的真實數據(依據一個計算公式)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM