• Django


    Django - WebSocket:dwebsocket

    什么是WebSocket

    WebSocket是一种在单个TCP连接上进行全双工通信的协议

    WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

    现在,很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

    而比较新的技术去做轮询的效果是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源。

    在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

    很可能用不到的判断

    WebSocket 协议在2008年诞生,2011年成为国际标准,所有浏览器都已经支持了。你可以这么判断浏览器是否支持:

    <script>
        if ('WebSocket' in window) {
            console.log('你的浏览器支持 WebSocket')
        }
    </script>
    

    WebSocket for Django

    django实现websocket大致上有两种方式,一种channels,一种是dwebsocket。channels依赖于redis,twisted等,相比之下使用dwebsocket要更为方便一些。

    Install dwebsocket

    pip install dwebsocket  # 最新版
    # 网上貌似说最新的不好用,我们可以下载大家使用较多的老版本
    pip install dwebsocket==0.4.2
    

    我开始就下的默认版本,然后报错:

    AttributeError: 'WSGIRequest' object has no attribute 'is_websocket'
    

    后来下载老版本就好了。

    服务端常用方法或者属性

    名称
    描述 备注
    @accept_websocket 处理websocket和HTTP请求 该装饰器用的较多
    @require_websocket 仅处理websocket请求,拒绝HTTP请求
    request.is_websocket() 如果请求类型是websocket,返回True,否则返回False 通常与@accept_websocket装饰器搭配
    request.websocket 当websocket请求建立后,该请求具有一个websocket属性,可以通过该属性进行通信, 如果request.is_websocket()是False,则这个属性为None。
    request.websocket.wait() 阻塞接收消息
    request.websocket.read() 非阻塞接收消息
    request.websocket.count_messages() 返回队列中的消息数量
    request.websocket.has_messages() 如果有新消息返回True,否则返回False
    request.websocket.send() 向客户端发送bytes类型的数据
    request.websocket.close() 服务器端主动关闭websocket服务
    request.websocket.iter() websocket迭代器

    客户端的属性和方法

    名称 类型 描述
    WebSocket 对象 提供到服务端的双向通道
    onopen 属性 当websocket连接时调用的事件处理程序
    onmessage 属性 通知接收到消息的事件处理程序
    onerror 属性 当出现错误时调用的事件处理程序
    onclose 属性 当套接字关闭时调用的事件处理程序
    readState 属性 报告websocket连接状态
    close 方法 关闭websocket
    send 方法 使用websocket向服务端发送数据
    url 属性 报告套接字的当前URL
    protocol 属性 报告服务器所选中的协议
    binaryType 属性 由onmessage接收的二进制数据格式
    bufferedAmount 属性 使用send的已排队的数据字节数
    extensions 属性 包括服务器所选中的扩展名

    关于readState,根据readState属性可以判断websocket的连接状态,该属性的值可以是以下几种:

    属性值 对应常量 描述 备注
    0 CONNECTING 正在建立连接 但还没有建立完毕
    1 OPEN 连接成功建立,可以进行通信
    2 CLOSING 连接正在关闭 即将关闭
    3 CLOSED 连接已关闭 或者根本没有建立连接

    根据bufferedAmount可以知道有多少字节的数据等待发送,若websocket已经调用了close方法该属性将会一直增长。

    必要的settings配置

    # settings.py
    MIDDLEWARE_CLASSES = [
        'dwebsocket.middleware.WebSocketMiddleware'
    ]
    WEBSOCKET_ACCEPT_ALL=True  # 可以允许每一个单独的视图实用websocket
    

    添加上这个中间件,就会拒绝单独的视图使用websocket,不过我们一般都是使用视图搭配websocket,所以,这个配置忘掉吧,顺便把第二个配置也忘掉,除非你要搞复杂的操作......

    示例

    环境

    django1.11 + Python3.6 + PyCharm2018.1 + win10

    Django中的配置

    settings中保持默认即可

    # urls.py
    from django.conf.urls import url
    from django.contrib import admin
    from web import views   # web是我的APP名称
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^test/', views.test, name='test'),
    ]
    

    views.py

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    
    # from django.shortcuts import render
    
    # Create your views here.
    import json
    import time
    
    
    from django.shortcuts import render
    from dwebsocket.decorators import accept_websocket
    
    
    @accept_websocket
    def test(request):
    
        if request.is_websocket():
            print "ws flag"
            print('websocket connection....')
            # msg = request.websocket.wait()  # 接收前端发来的消息
            # print help(request.websocket)
            # msg = request.websocket
            # print(msg, type(msg), json.loads(msg))  # b'["1","2","3"]' <class 'bytes'> ['1', '2', '3']
            i = 0
            while 1:
                msg = request.websocket.wait()  # 接收前端发来的消息
                if msg:
                    # 你要返回的结果
                    # for i in range(10):
                    # request.websocket.send('service message: {}'.format(i).encode())  # 向客户端发送数据
                    request.websocket.send(raw_input("Please Enter Informaiton :").encode())
                    # time.sleep(0.5)  # 每0.5秒发一次
                    i += 1
                else:
                    request.websocket.close()
        else:  # 如果是普通的请求返回页面
            print "http flag"
            return render(request, 'test.html')
    

    test.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>test</title>
    </head>
    <body>
    <div></div>
    
    </body>
    <!-- 首先引入 jQuery -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script>
        // 判断浏览器是否支持WebSocket,目前应该所有的浏览器都支持了.....
        if ('WebSocket' in window) {
            console.log('你的浏览器支持 WebSocket')
        }
    	// 创建一个WebSocket对象:sk,并且建立与服务端的连接(服务端程序要跑着哦)
        var sk = new WebSocket('ws://' + window.location.host + '/test/');
        // 向服务端发送消息
        sk.onopen = function () {
            console.log('websocket connection successful...');
            var l = ['1', '2', '3'];
            sk.send(JSON.stringify(l));
        };
        // 接收服务端的消息,主要的业务逻辑也在这里完成
        sk.onmessage = function (msg) {
            // 业务逻辑
            html = "<p>" + msg.data + "</p>";
            $("div").append(html);
            console.log('from service message: ', msg.data);
            // 由于服务端主动断开连接,这里也断开WebSocket连接
            if (sk.readyState == WebSocket.CLOSED) sk.close();
        };
        // 完事就关闭WebSocket连接
        sk.onclose = function (msg) {
            console.log('websocket connection close...');
            sk.close()
        };
        // 当WebSocket连接创建成功后,我们就可以向服务端发送数据了
        if (sk.readyState == WebSocket.OPEN) sk.onopen();
    
    </script>
    </html>
    

    测试

    测试网站:
    http://www.websocket-test.com/

    原文:https://www.cnblogs.com/Neeo/articles/11551731.html

  • 相关阅读:
    LeetCode 面试题 02.02. 返回倒数第 k 个节点
    LeetCode 1290. 二进制链表转整数
    LeetCode 面试题52. 两个链表的第一个公共节点
    LeetCode 2. 两数相加
    Jupyter Notebook 常用快捷键 (转)
    LeetCode 414. 第三大的数
    LeetCode 404. 左叶子之和
    三年了
    LeetCode 543. 二叉树的直径
    求结点在二叉排序树中层次的算法
  • 原文地址:https://www.cnblogs.com/geoffreygao/p/13679465.html
Copyright © 2020-2023  润新知