Skip to content

dwebsocket

什么是WebSocket

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

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

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

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

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

很可能用不到的判断

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

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

WebSocket for Django

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

Install

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

服务端常用方法或者属性

名称描述备注
@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的连接状态,该属性的值可以是以下几种:

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

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

必要的settings配置

settings.py

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

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

示例

环境

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

Django中的配置

settings中保持默认即可

urls.py

python
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

python
import time
from django.shortcuts import render
from dwebsocket.decorators import accept_websocket


@accept_websocket
def test(request):
    if request.is_websocket():
        print('websocket connection....')
        msg = request.websocket.wait()  # 接收前端发来的消息
        print(msg, type(msg), json.loads(msg))  # b'["1","2","3"]' <class 'bytes'> ['1', '2', '3']
        while 1:
            if msg:
                for i in range(10):
                    request.websocket.send('service message: {}'.format(i).encode())  # 向客户端发送数据
                    time.sleep(0.5)  # 每0.5发一次
                request.websocket.close()
    else:  # 如果是普通的请求返回页面
        return render(request, 'test.html')

test.html

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>

see also:websocket知识汇总以及websocket在Django中的实现 | dwebsocket 0.5.11 | 利用dwebsocket在Django中使用Websocket | django中使用dwebsocket 后端实时进行和前端通讯 | Django实现websocket完成实时通讯、聊天室、在线客服等