dwebsocket基本使用

2,117 阅读4分钟

这是我参与11月更文挑战的第30天,活动详情查看:2021最后一次更文挑战

目的:实现websocket请求,客户端和服务端实现双工通信(客户端与服务端通过websocket连接成功后,可以双向地主动发消息)

  • 与django推荐的channel不同,dwebsocket使用更加方便简单
  • channels依赖于redis,twisted等,相比之下使用dwebsocket要更为方便一些

WebSocket的工作流程是这 样的:浏览器通过JavaScript向服务端发出建立WebSocket连接的请求,在WebSocket连接建立成功后,客户端和服务端就可以通过 TCP连接传输数据。因为WebSocket连接本质上是TCP连接,不需要每次传输都带上重复的头部数据,所以它的数据传输量比轮询和Comet技术小了很多.

dwebsocket 相关网站

安装dwebsocket

pip3 install dwebsocket

注册APP

注册之后才能使用dwebsocket

INSTALLED_APPS = [
    .....
    .....
    'dwebsocket',
]

使用

使用的方法有两种情况,如下:

  • 第一种则是在配置文件中设置中间件,配置所有视图都可以接收使用websocket功能。

  • 第二种则是利用修饰器的方式单独对某个视图进行增加websocket功能。

第一种:全局配置

在django项目的配置文件的中间件中配置,这种情况下需要每一个视图函数都使用websocket通信,否则报错。

MIDDLEWARE_CLASSES = [
    ......
    ......
    'dwebsocket.middleware.WebSocketMiddleware'  # 为所有的URL提供websocket,如果只是单独的视图需要可以不选
 
]
WEBSOCKET_ACCEPT_ALL=True   # 可以允许每一个单独的视图实用websockets

第二种:装饰器单一视图使用

只需views.py文件中,将对应的视图函数添加装饰器

  • accept_websocket-—可以接受websocket请求和普通http请求
  • require_websocket----只接受websocket请求,拒绝普通http请求

如果使用了require_websocket,但使用了普通http请求,则出现Bad Request的报错信息

补充相关方法

相关方法函数说明
1.request.is_websocket()
如果是个websocket请求返回True,如果是个普通的http请求返回False,可以用这个方法区分它们。

2.request.websocket
在一个websocket请求建立之后,这个请求将会有一个websocket属性,用来给客户端提供一个简单的api通讯,如果request.is_websocket()是False,这个属性将是None3.WebSocket.wait()
返回一个客户端发送的信息,在客户端关闭连接之前他不会返回任何值,这种情况下,方法将返回None

4.WebSocket.read()
如果没有从客户端接收到新的消息,read方法会返回一个新的消息,如果没有,就不返回。这是一个替代wait的非阻塞方法

5.WebSocket.count_messages()
返回消息队列数量

6.WebSocket.has_messages()
如果有新消息返回True,否则返回False

7.WebSocket.send(message)
向客户端发送消息

8.WebSocket.__iter__()
websocket迭代器

案例演示1

视图函数使用装饰器的方式,浏览器客户端与服务端使用websocket通信,增加websocket停止以及重连功能

views.py

from django.shortcuts import render
from dwebsocket import accept_websocket, require_websocket
import time, json


@accept_websocket
def test_websocket(request):
    if request.is_websocket(): # 如果请求是websocket请求:
        WebSocket = request.websocket
        i = 0 # 设置发送至前端的次数
        messages = {}

        while True:
            i += 1 # 递增次数 i
            time.sleep(1) # 休眠1秒

            # 判断是否通过websocket接收到数据
            if WebSocket.has_messages():

                # 存在Websocket客户端发送过来的消息
                client_msg = WebSocket.read().decode()
                # 设置发送前端的数据
                messages = {
                    'time': time.strftime('%Y.%m.%d %H:%M:%S', time.localtime(time.time())),
                    'server_msg': 'send %d times!' % i,
                    'client_msg': client_msg,
                }

            else:
                # 设置发送前端的数据
                messages = {
                    'time':time.strftime('%Y.%m.%d %H:%M:%S',time.localtime(time.time())),
                    'server_msg': 'send %d times!' % i,
                }

            # 设置发送数据为json格式
            request.websocket.send(json.dumps(messages))
    else:
        return render(request, 'test_websocket.html')

test_websocket.html

【坑】:注意发送ws请求时,url后面加上"/"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            // 点击连接websocket按钮,则启动访问websocket
            $('#connect_websocket').click(function () {
                // 点击连接websocket按钮,则启动访问websocket
                $('#connect_websocket').click(function () {

                    if(window.s){
                        window.s.close()
                    }

                    // 设置websocket的服务端url
                    var s = new WebSocket("ws://" + window.location.host + "/test_websocket/");

                    // 打开连接websocket服务,连接成功则打印信息
                    s.onopen = function () {
                        console.log('WebSocket open');//成功连接上Websocket
                    };

                    // 接收服务端发送过来的数据,在浏览器上刷新
                    s.onmessage = function (e) {
                        console.log('message: ' + e.data);//打印出服务端返回过来的数据
                        $('#messagecontainer').prepend('<p>' + e.data + '</p>');
                    };

                    window.s = s;
                });

                // 点击发送消息按钮,则通过websocket发送数据至服务端
                $('#send_message').click(function () {
                    if (!window.s) {
                        alert("Please connect server.");
                    } else {
                        window.s.send($('#message').val());//通过websocket发送数据
                    }
                });

                // 点击关闭websocket连接
                $('#close_websocket').click(function () {
                    if (window.s) {
                        window.s.close();//关闭websocket
                        console.log('websocket is closed!');
                    }
                });

            });
        });
    </script>
</head>
<body>

<input type="text" id="message" value="Open websocket!" />
<button type="button" id="connect_websocket">连接websocket</button>
<button type="button" id="send_message">发送 message</button>
<button type="button" id="close_websocket">关闭websocket</button>
<h1>Received Messages</h1>
<div id="messagecontainer"></div>

</body>
</html>

urls.py

from django.conf.urls import url

from app01 import views

urlpatterns = [  
    url(r'^test_websocket/', views2.test_websocket),
]

演示2:websocket连接成功后,服务端主动发消息

views.py

修改上述视图文件

from django.shortcuts import render
from dwebsocket import accept_websocket, require_websocket
import time, json


@accept_websocket
def test_websocket(request):
    if request.is_websocket(): # 如果请求是websocket请求:
        WebSocket = request.websocket
       
        time.sleep(3)	# 建立websocket连接3s后发一个推送消息
        messages = {'name': 'jack'}
       	WebSocket.send(json.dumps(messages))

    else:
        return render(request, 'app02-login.html')

dwebsocket的坑

背景:客户端无法自动断开websocket链接,导致django无法处理上一次遗留的websocket客户端从而引起的报错。

方法:调用window.beforunload,在浏览器

    window.onbeforeunload = function () {
        ws.close()
        console.log(1);//在刷新页面或者关闭页面需要断开websocket
    };

刷新,或者关闭页面的时候,自动关闭websocket链接

结语

文章首发于微信公众号程序媛小庄,同步于掘金

码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)