截至目前为止,我们所接触到的项目内部都是基于HTTP协议实现通信的:http协议是无链接无状态,客户端发送请求,服务端返回响应,服务端不会自动朝客户端发送消息。

有三种方式实现服务端主动向客户端推送消息:

1.轮询
2.长轮询
3.websocket

轮询

效率低、基本不用

让浏览器定时朝后端发送请求(通过ajax向后端偷偷发送数据),比如每隔五秒钟发一次请求,那么你的数据延迟就可能会高达五秒

不足之处
  数据延迟
  消耗资源过大
  请求次数太多

长轮询

兼容性好,一般大公司都会考虑使用它

# 队列+ajax
服务端给每个客户端建立队列,让浏览器通过ajax朝服务端要数据,去各自的队列中获取
如果没有数据则会阻塞但是不会一直阻塞,比如阻塞你30秒,还没有数据则返回,然后让客户端浏览器再次发送请求数据的请求。
让浏览器内部偷偷的朝服务端获取数据,客户端第一次来的时候会给每一个客户端创建一个独有的队列,之后客户端请求数据都是从自己对应的队列中索要,
由于队列没有数据的时候,get方法会阻塞一旦有数据又会立刻运行,所以我们利用timeout参数加异常捕获的方式来做到基本零延迟
相对于轮询 基本是没有消息延迟的 请求次数降低了很多
# web版本的qq和微信基本上用的都是这么一个逻辑

基于ajax及队列实现的长轮询的功能(django简易版的聊天室)

"""
1.首页自定义用户唯一表示,给每个用户初始化一个队列
2.发送按钮绑定点击事件 后端讲数据放入每一个队列中
3.书写自动获取数据的ajax代码 循环调用
4.前端获取数据DOM操作渲染页面
"""

urls.py

#基于ajax+队列实现长轮询,聊天页面
url(r'^ab_bl/', views.ab_bl),
#前端给后端发送信息
url(r'^send_msg/', views.send_msg),
#展示前端的信息
url(r'^get_msg/', views.get_msg),

ab_bl.html

<body>
<h1>{{ name }}聊天室</h1>
<p>
  <input type="text" name="content" id="d2">
  <button id="d1">发送</button>
</p>
<h1>聊天记录</h1>
<div id="content"></div>

<script>
  //点击发送触发ajax
  $('#d1').click(function () {
      $.ajax({
          url:'/send_msg/',  //消息发送地址,后端
          type:'post',
          data:{'content':$('#d2').val()},  //传递给后端的信息
          success:function (data) {  //后端返回给前端的消息

          }
      })
  });

  function getMsg(){
        $.ajax({
            url:'/get_msg/',
            type:'get',
            data:{'name':'{{ name }}'},  // 只要当前登陆人的队列中的数据
            success:function (args) {
                // 针对返回的消息做相应的处理
                if(args.status){
                    // 有消息则渲染页面  讲消息全局放到聊天纪录里面
                    // 1 创建标签
                    var pEle = $('<p>');
                    // 2 给标签设置文本内容
                    pEle.text(args.msg);
                    // 3 讲创建好的标签添加到聊天记录div标签内
                    $('#content').append(pEle)
                }else{
                    // 没有消息 则继续发送
                }
                getMsg()  // 循环请求数据
            }

        })
    }
  $(function () {
      getMsg()  //页面加载完毕自动执行
  })
</script>
</body>

views.py

from django.shortcuts import render,HttpResponse
import queue
from django.http import JsonResponse
# 全局大字典
q_dict = {}  # {'唯一表示':队列,....}

def ab_bl(request):
    #从路径传参name,用于做每个用户的标识
    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':
        #获取用户发送的消息:ajax中data传递的数据
        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)

websocket

真正的做到服务端发送消息而不再是被动的发送

目前主流的浏览器都是支持websocket

HTTP协议  网络协议(不加密传输)
HTTPS协议 网络协议(加密传输)
    上面两个协议都是短链接

websocket网络协议  (加密传输)
    浏览器和服务端创建链接之后 默认不再断开
    两端都可以基于该链接收发消息
    websocket的诞生能够真正做到服务端发送消息而不再是被动的发送

websocket内部原理

分成两大部分
    1.握手环节:验证服务端是否支持websocket协议
        第一次访问服务端的时候(基于http协议) 浏览器产生一个随机字符串放在请求头中给服务端发送一份,自己留一份
        Sec-WebSocket-Key: ePW8kp1XqLNWbJxE/Q38SA==
        服务端和客户端都对随机字符串做下面的操作

        随机字符串 + magic string拼接
        然后再讲拼接好的结果进行加密处理(sha1/base64)的到密文
        
     服务端将产生的密文通过响应头再次发送给客户端浏览器 浏览器自动比对双方产生的密文是否一致,如果一致说明服务端支持websocket 如果不一致会报错 假设比对上了 建立websocket链接 基于该链接收发消息
2.收发数据 密文传输 >>> 必然要涉及解密(全球统一)的过程 基于网络传输的数据都是二进制格式 对应到我们python中就是bytes类型 数据解密过程 1.对收到的消息,先读取数据的第2个字节的后7位(payload)字节 根据7位数据的大小来指定不同的解密流程 =127:再往后读取8个字节 =126:再往后读取2个字节 <=125:不再往后读取 除去前面读取的数据之外 再往后读4个字节(masking-key) 拿着它去解析后面的真实数据(依据一个计算公式)

代码验证

后端代码无需掌握,前端的就行

<!--前端只需要写一行代码就可以了-->
<script>
    var ws = new WebSocket('ws://127.0.0.1:22/')
</script>

<!--通过ws对象点send方法即可实现websocket的数据交互-->
02-14 01:46