美文网首页
Tornado-长轮询_webSocket

Tornado-长轮询_webSocket

作者: Python野路子 | 来源:发表于2018-07-14 13:48 被阅读0次

    长轮询

    1. 轮询(Polling):是指不管服务器端有没有更新,客户端(通常是指浏览器)都定时的发送请求进行查询,轮询的结果可能是服务器端有新的更新过来,也可能什么也没有,只是返回个空的信息。不管结果如何,客户端处理完后到下一个定时时间点将继续下一轮的轮询。类似去不断查询银行支付状态。
    2. 推送或叫长连接(Long-Polling):这种方式的服务其客户端是不做轮询的,客户端在发起一次请求后立即挂起,一直到服务器端有更新的时候,服务器才会主动推送信息到客户端。 在服务器端有更新并推送信息过来之前这个周期内,客户端不会有新的多余的请求发生,服务器端对此客户端也啥都不用干,只保留最基本的连接信息,一旦服务器有更新将推送给客户端,客户端将相应的做出处理,处理完后再重新发起下一轮请求。
      优点:在无消息的情况下不会频繁的请求。
      缺点:服务器hold连接开销大,浪费资源,消耗流量。
      实例:WebQQ、Hi网页版、Facebook IM。

    WebSocket

    轮询与长轮询都是基于HTTP的,两者本身存在着缺陷:轮询需要更快的处理速度;长轮询则更要求处理并发的能力;两者都是“被动型服务器”的体现:服务器不会主动推送信息,而是在客户端发送ajax请求后进行返回的响应。而理想的模型是"在服务器端数据有了变化后,可以主动推送给客户端",这种"主动型"服务器是解决这类问题的很好的方案。Web Sockets就是这样的方案。
    由于长轮询消耗太多资源,主要原因是客户端和服务器并没有连接在一起,能够让客户端和服务器一直保持连接,这就需要用到websocket。

    应用场景:实现即时通讯:如股票交易行情分析、聊天室、在线游戏等,替代轮询和长轮询。

    优点

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

    介绍:
    WebSocket 协议是基于 TCP 的一种新的 HTML5 网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
    简单说:客户端和服务器一直连接在一起。

    WebSocket客户端编程

    浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title> WebSocket </title>
        <style>
            *{
                margin: 0;
                padding: 0;
            }
            .box{
                width: 800px;
                margin-left: auto;
                margin-right: auto;
                margin-top: 25px;
            }
            #text{
                width: 685px;
                height: 130px;
                border: 1px solid skyblue;
                border-radius: 10px;
                font-size: 20px;
                text-indent: 1em;
                resize:none;
                outline: none;
            }
            #text::placeholder{
                color: skyblue;
            }
            .btn{
                width: 100px;
                margin: -27px 0 0px 8px;
            }
            #messages{
                padding-left: 10px;
                font-size: 25px;
            }
            #messages li{
                list-style: none;
                color: #000;
                line-height: 30px;
                font-size: 18px;
    
            }
        </style>
    </head>
    <body>
        <div class="box">
            <div>
                <textarea id="text" placeholder="请输入您的内容"></textarea>
                <a href="javascript:WebSocketSend();" class="btn btn-primary">发送</a>
            </div>
            <ul id="messages">
            </ul>
        </div>
        <script type="text/javascript">
            var mes = document.getElementById('messages');
    
            if('WebSocket' in window){  /*判断浏览器是否支持WebSocket接口*/
                /*创建创建 WebSocket 对象,协议本身使用新的ws://URL格式*/
                var Socket = new WebSocket("ws://127.0.0.1:8000/websocket")
                /*连接建立时触发*/
                Socket.onopen = function () {
                    alert("连接已建立,可以进行通信")
                };
                /*客户端接收服务端数据时触发*/
                Socket.onmessage = function (ev) {
                    var received_msg = ev.data; /*接受消息*/
                    var aLi = "<li>" + received_msg + "</li>";
                    mes.innerHTML += aLi;
                    /*jq方式*/
                    // $(mes).append($(aLi));
                };
                /*连接关闭时触发*/
                Socket.onclose = function () {
                    mes.innerHTML += "<br>连接已经关闭...";
                };
            }else{
                /*浏览器不支持 WebSocket*/
                alert("您的浏览器不支持 WebSocket!");
            }
            function WebSocketSend() {
                /*form 里的Dom元素(input select checkbox textarea radio)都是value*/
                var send_msg = document.getElementById('text').value;
                //或者JQ中获取
                // var send_msg = $("#text").val();
                /*使用连接发送消息*/
                Socket.send(send_msg);
            }
        </script>
    </body>
    </html>
    

    WebSocket服务端编程

    import tornado.httpserver
    import tornado.ioloop
    import tornado.options
    import tornado.web
    from data.user_module import User
    from tornado.options import define,options
    
    from tornado.web import authenticated  #导入装饰器
    from pycket.session import SessionMixin
    
    import tornado.websocket
    
    import datetime
    
    define('port', default=8080, help='run port', type=int)
    
    
    #设置BaseHandler类,重写函数get_current_user
    class BaseHandler(tornado.web.RequestHandler, SessionMixin):
        def get_current_user(self): #前面有绿色小圆圈带个o,再加一个箭头表示重写
            print('********')
            current_user = self.session.get('user') #获取加密的cookie
            print(current_user)
            if current_user:
                return current_user
            return None
    
    
    #基类
    class BaseWebSocketHandler(tornado.websocket.WebSocketHandler,SessionMixin):
        def get_current_user(self):
            current_user = self.session.get('user')
            if current_user:
                return current_user
            return None
    #跳转
    class IndexHandler(BaseHandler):
        @authenticated
        def get(self):
            self.render('websocket.html')
    
    
    
    #在LoginHandler类中的post函数里面加上设置cookie
    class LoginHandler(BaseHandler):
        def get(self):
            nextname = self.get_argument('next','') #将原来的路由赋值给nextname
            print(nextname)
            self.render('login.html',nextname=nextname) #跳转页面带上获取的参数
    
        def post(self,*args,**kwargs):
            user = self.get_argument('name','')
            username = User.by_name(user)
            passwd = self.get_argument('password','')
    
            nextname = self.get_argument('next','') #获取之前页面的路由
    
            if username and passwd == username[0].password:
                self.session.set('user', user)  # 设置加密cookie
                self.redirect(nextname)     #跳转到之前的路由
            else:
                self.render('login.html',nextname=nextname)
    
    
    
    '''Tornado 定义了 tornado.websocket.WebSocketHandler 类用于处理 WebSocket 链接的请求,
    应用开发者应该继承该类并实现其中的open()、on_message()、on_close() 函数
    '''
    class MessageHandler(BaseWebSocketHandler):
        users = set()  #避免重复登录,利用集合的去重性
    
        def open(self):   #当一个新的WebSocket连接建立后被调用。
            MessageHandler.users.add(self)   #类属性,用类名直接调用,
            print('----------open------')
    
        def on_message(self, message): #当客户端发送消息message过来时被调用,注意此方法必须被重写。
            print(self.request.remote_ip)
            for u in self.users:   #write_message,向客户端发送消息messagea,message可以是字符串或字典(字典会被转为json字符串
                u.write_message('%s-%s-说:%s'%(self.current_user,
                                              datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                                              message
                                              ))
    
        def on_close(self): #当WebSocket连接关闭后被调用。
            if self in MessageHandler.users:
                MessageHandler.users.remove(self)   #断开的时候移除
                print(self)
            print('-----------close--------')
    
    
    
    
    
    application = tornado.web.Application(
        handlers = [
            (r'/login',LoginHandler),
            (r'/websocket',MessageHandler),
            (r'/',IndexHandler),
        ],
        # cookie_secret='1q2w3e4r',
        #设置跳转路由,为了防止在没有登录情况下,直接输入需要登录才可见的url进行访问,做判断,如果没有登录则跳转到这个路由下
        login_url='/login',
        pycket={
            'engine': 'redis',
            'storage': {
                'host': 'localhost',
                'port': 6379,
                'db_sessions': 5,
                'db_notifications': 11,
                'max_connections': 2 ** 31,
            },
            'cookies': {
                'expires_days': 30,
                'max_age': 100
            },
        },
        static_path='static',
        template_path = 'templates', #想要Tornado能够正确的找到html文件,需要在 Application 中指定文件的位置
        cookie_secret='sgsdf',
        debug = True   #调试模式,修改后自动重启服务,不需要自动重启,生产情况下切勿开启,安全性
    )
    
    if __name__ == '__main__':
        tornado.options.parse_command_line()
        http_server = tornado.httpserver.HTTPServer(application)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.instance().start()
    

    运行效果:


    image.png
    image.png

    Tornado 定义了 tornado.websocket.WebSocketHandler 类用于处理 WebSocket 链接的请求,应用开发者应该继承该类并实现其中的open()、on_message()、on_close() 函数,除了这3个 Tornado 框架自动调用的入口函数,WebSocketHandler 还提供了两个开发者主动操作 WebSocket的函数:
    WebSocketHandler.write_message(message)函数:用于向与本链接相对应的客户端写消息。
    WebSocketHandler.close(code=None,reason=None)函数:主动关闭 WebSocket链接。其中的code和reason用于告诉客户端链接被关闭的原因。参数code必须是一个数值,而reason是一个字符串。

    相关文章

      网友评论

          本文标题:Tornado-长轮询_webSocket

          本文链接:https://www.haomeiwen.com/subject/craepftx.html