• websocket知识汇总以及websocket在Django中的实现(转载)


            最近在完成项目中需要用到实时技术,项目需求是将后端的一个文件内容实时读取然后发送到前端.这里主要涉及到两个技术.一个是后端如何实时读取一直在更新中的数据,另一点是如何保证web前后端的通讯,能将读取到的数据实时传送给前端.

            由于主要是进行后端开发,前端涉及的少,趁这个机会刚好学习了一下前端的一些知识.

    一.Ajax轮询

            最开始解决实时通讯Google后使用了ajax的轮询技术,如果说从要求上来看基本满足要求,代码也十分简单,核心代码段如下:

    1. var getting = {
    2. url:"{% url '...' %}",
    3. type:'GET',
    4. dataType: 'text',
    5. success: function(data) {
    6. $("#output").val(data);
    7.                         //sleep(1000);
    8. $.ajax(getting)
    9. }
    10. }
    11. };
    12. $.ajax(getting);

            也就是ajax异步请求成功后再次调用自己发送get请求,这样可以满足"实时"获取到后端一直在更新的文件的最新内容.但是,有个很严重的问题就是前端一直在get请求,导致极大的占用带宽,占用服务器的处理资源.后来加入修改,将每次发送的请求间隔1s,发现仍然是十分浪费带宽资源.

            后来发现有ajax长轮询技术,就是在每次发送请求后,如果后台有数据,则将数据返回,前端拿到后继续发送请求,如果后台没有数据,就不会response.可以很好的优化ajax的轮询缺陷,但是感觉该方法还是不够好.

            传统的request必须有一个response,对于实时通讯,这样是很差劲的体验.服务器端在建立连接后更新到新数据时无法主动给前端发送response,不够理想.

    二.弃用ajax轮询,选择websocket

            后来Google到一个稍微新一点的技术,websocket,刚开始以为是socket的孪生表弟,后来才发现它俩关系跟java和JavaScript一样.有一篇文章讲的很好,入门了解一看便知,websocket基础

            websocket相比较传统http的优势很明显,借阮老师的一张图来看:

                              

    一张图就明白了它的优势有多大.接下来就是开干

            先看看前端主要部分代码:

    1. var socket = new WebSocket("ws://" + window.location.host + "{% url '...' %}");
    2. window.s = socket;
    3. window.s.onopen = function () {
    4.                 ...
    5. console.log('websocket conneted!')
    6. };
    7. window.s.onmessage = function (event) {
    8.                     ...
    9. }
    10. };
    11.             window.s.onclose = function(){
    12.                 ...
    13.             }
            这样,你就创建了一个websocket连接,浏览器是否支持需要你写个判断,我用的Chrome,直接支持websocket.其他具体说明请问度娘或谷大哥.

            后台也需要接受,由于我用的是Django,后来了解到Django channels也可以实现实时通讯,同时也发现了一个dwebsocket第三方库也是可以实现实时通讯,其实都是用了websocket技术而已...看你个人喜好,我使用了dwebsocket,因为Django channels安装的不少,还需要加到INSTALLED_APPS中,我只是需要用到websocket技术而已,dwebsocket相比较之下非常轻便,直接pip install dwebsocket就可以用,符合人生苦短,我用python的编程思想.其实我是懒

    三.使用dwebsocket     

        一.使用方法   

            使用上很方便,如果为一个单独的视图函数处理一个websocklet连接可以使用accept_websocket装饰器,它会将标准的HTTP请求路由到视图中。使用require_websocke装饰器只允许使用WebSocket连接,会拒绝正常的HTTP请求。
            在设置中添加设置MIDDLEWARE_CLASSES=dwebsocket.middleware.WebSocketMiddleware这样会拒绝单独的视图实用websocket,必须加上accept_websocket 装饰器。设置WEBSOCKET_ACCEPT_ALL=True可以允许每一个单独的视图实用websockets.....当然,在settings中这样做完全没必要,因为我就是一个视图函数来处理请求的,无需复杂化.直接在视图函数处引入提供的装饰器accept_websocket即可.

        二.一些属性和方法

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

            2.request.websocket
            在一个websocket请求建立之后,这个请求将会有一个websocket属性,用来给客户端提供一个简单的api通讯,如果request.is_websocket()是False,这个属性将是None。

            3.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迭代器

    必须要学习了解才知道我们需要用到什么.当然了,客户端的也需要了解一下:

                                         

            这是客户端的一些说明,在客户端,websocket的两个属性:readyState和bufferedAmount,区别和说明如下:

                    根据readyState属性可以判断webSocket的连接状态,该属性的值可以是下面几种:
                    0 :对应常量CONNECTING (numeric value 0),
                     正在建立连接连接,还没有完成。The connection has not yet been established.
                     1 :对应常量OPEN (numeric value 1),
                     连接成功建立,可以进行通信。The WebSocket connection is established and communication is possible.
                    2 :对应常量CLOSING (numeric value 2)
                     连接正在进行关闭握手,即将关闭。The connection is going through the closing handshake.
                    3 : 对应常量CLOSED (numeric value 3)
                     连接已经关闭或者根本没有建立。The connection has been closed or could not be opened.

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

            好了,其实也就这点内容,接下来就开始逻辑实现了,我在服务器端部分代码如下:

    1. @method_decorator(accept_websocket)
    2. def get(self, request):
    3. if request.is_websocket():
    4. for message in request.websocket:
    5. if ...:
    6.                     ...
    7. else:
    8. request.websocket.send(...)
    9. else:
    10.             .....
    11. return HttpResponse('点个赞')

            这里我用到了method_decorator,这个可以忽略,因为我这个是视通函数,不是的话直接用@accept_websocket就行

            注意这里request.websocket.send发送的内容格式需要为bytes,而不是str..同时需要判断请求,用request.is_websocket()来进行判断.这里用for message in request.websocket比较好,前端发来的信息都会在里面,分析源码后感觉比wait()方法要好用,源码如下:

    1. while True:
    2. message = self.wait()
    3. yield message
    4. if message is None:
    5. break
            会处理多条message.具体后台的业务逻辑就可以根据需求添加了

    四.websocket心跳包机制

            接下来还缺一个需求,就是如何判断前后端是否是连接状态,否则一方端口另一方会无法察觉,而继续发送,以至于报错.分析了一下这种长连接是哪方进行心跳包发送比较好,理论上来说都应该间隔性主动发送,但是,考虑到服务器端的带宽资源性问题,以及重要性问题,还是由客户端主动定期发送比较合适.同时发现,当客户端断开连接,刷新退出浏览器时,for message in request.websocket中的message会返回None,根据message返回的值来处理逻辑问题.那样,就剩下客户端需要加入心跳包了

            废话少说,主要代码如下:

    1. function keepalive(ws) {
    2. var time = new Date();
    3. if ((time.getTime() - last_health > 35000)) {
    4. //ws.close();
    5. //clearInterval(window.heartbeat_timer)
    6. } else {
    7. if (ws.bufferedAmount == 0 && ws.readyState == 1) {
    8. ws.send('ping')
    9. }
    10. }
    11. }

            time.getTime() - last_health > 35000表示现在时间与上次接受到服务器端数据时间相差大于35s,就ws.close()断开连接,或者重连或者实现其他业务逻辑都可...否则,判断ws.bufferedAmount == 0 && ws.readyState == 1,表示没有拔网线,即无阻塞,且是联通状态,发送心跳包,ping,服务器收到后会回复个pong,随个人喜好.在onopen中就需要加入间隔性发送的代码:

    1. window.s.onopen = function () {
    2. window.heartbeat_timer = setInterval(function () {keepalive(window.s)}, 30000);

            这里将心跳包间隔设置为30s,别忘了在onmessage中每次收到服务器消息都要刷新收到消息的时间.这样,就实现了客户端和服务器之间的心跳包重连.

    五.后记

            对于如何用python进行文件的实时读取并发送前端有时间会记录下来.

            



















    原文链接

  • 相关阅读:
    Merge Sort
    important在css中的作用
    Quick Sort
    Shell's Sort
    2019年7月22日~28日 第四周学习记录
    eclipse安装心得和环境变量配置的体会
    2019年7月15日~7月21日 第三周学习记录
    2019年7月1日~7月7日 第一周学习记录
    程序为本,代码方式仅是语言 ———《大道至简软件工程实践者的思想》
    2019年7月8日~7月14日 第二周学习记录
  • 原文地址:https://www.cnblogs.com/python001-vip/p/12703876.html
Copyright © 2020-2023  润新知