10websocket


01长轮询

在网页,我们经常扫码登录,结合之前的学习的知识点,来思考下,前端是如何知道用户在手机上扫码登录了呢?

长轮询

客户端不断的向服务器发送请求 

缺点

  1. 开销大
  2. 浪费资源
  3. 消耗流量

02websocket介绍

长轮询消耗太多资源,其中主要原因是客户端和服务端并没有一直连接在一起,如果能够让客户端和服务器一直保持连接呢?

正经介绍

WebSocket 协议是基于 TCP 的一种新的 HTML5 网络协议。

它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端

WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范

简单说

客户端和服务器一直连接在一起


03websocket服务端编程

刚才介绍了websocket,那么该如何使用呢?

第一步:导入

import tornado.websocket

第二步:基类

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

第三步:跳转 Handler

class IndexHandler(BaseHandler):
    @authenticated
    def get(self):
        self.render('08websocket.html')

第四步: websocket的Handler

class MessageWSHandler(BaseWebSocketHandler):
    users = set()   # 创建一个保存已登录的用户信息的集合(不能重复 唯一),因用户不能重复登录

    # 和客户端建立websocket连接时(即有新的聊天用户进入) 被调用
    def open(self):
        # 当有新的用户上线,将该用户加入集合中
        MessageWSHandler.users.add(self)

    # 处理消息 接收到消息时 (即聊天消息有更新) 被调用
    def on_message(self, message):
         # 将在线用户发送的消息通过服务器转发给所有的在线用户
        for u in self.users:
            # 向客户端发送消息时 需要调用这个方法
            u.write_message('%s-%s-说:%s' % (
                self.current_user,  # 当前用户
                datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                message
            ))

    # 断开连接时(当有用户离开) 被调用
    def on_close(self):
        if self in MessageWSHandler.users:
            MessageWSHandler.users.remove(self)  # 移除用户
方法 作用
open 新的WebSocket连接打开时被调用。
on_message 连接收到新消息时被调用
on_close 客户端关闭时被调用。
write_message 向客户端发送消息时被调用。
close 关闭连接时调用。

websocket

     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是一个字符串。

代码

import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpserver
from tornado.options import define, options
from tornado.web import authenticated
from data.user_module import User
import tornado.websocket
from pycket.session import SessionMixin
import datetime

define(name='port', default=8000, type=int, help='run port')


class BaseWebSocketHandler(tornado.websocket.WebSocketHandler, SessionMixin):
    def get_current_user(self):
        current_user = self.session.get('username')
        if current_user:
            return current_user
        return None


class BaseHandler(tornado.web.RequestHandler, SessionMixin):
    def get_current_user(self):
        current_user = self.session.get('username')
        if current_user:
            return current_user
        return None


class LoginHandler(BaseHandler):
    def get(self):
        next = self.get_argument('next', 'no')
        print(next)
        self.render('06cookie.html', nextname=next, error=None)

    def post(self):
        name = self.get_argument('name', 'no')
        user = User.by_name(name)
        passwd = self.get_argument('password', 'no')
        next = self.get_argument('next')
        print(next)
        if user and passwd == user.password:
            self.session.set('username', name)
            self.redirect(next)
        else:
            self.render('06cookie.html', nextname=next, error='用户名或密码错误')


class IndexHandler(BaseHandler):
    @authenticated
    def get(self):
        self.render('08websocket.html')


class MessageWSHandler(BaseWebSocketHandler):
    users = set()  # 创建一个存放在线用户的集合(不能重复)

    # 自定义的发送消息的函数
    def send_message(self, message):
        for user in self.users:
            user.write_message(message)

    # 当有新的聊天用户进入的时候  自动触发该函数
    def open(self):
        # 当有新的用户上线,将该用户加入集合中
        MessageWSHandler.users.add(self)
        print('-' * 20 + 'open' + '-' * 20)
        # 将新用户加入的信息发送给所有的在线用户
        self.send_message('%s上线' % self.current_user)

    # 当聊天消息有更新时  自动触发的函数
    def on_message(self, message):
        print(dir(self))
        print(self.request.remote_ip)  # 10.0.2.2
        print(message)  # 消息

        # 将在线用户发送的消息通过服务器转发给所有的在线用户
        self.send_message('%s-%s-说:%s' % (
            self.current_user,  # 当前用户
            datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            message
        ))

    # 当有用户离开时  自动触发的函数
    def on_close(self):
        if self in MessageWSHandler.users:
            MessageWSHandler.users.remove(self)  # 移除用户
            # 将该用户离开的消息发送给所有在线的用户
            self.send_message('%s下线~' % self.current_user)
        print('-' * 20 + 'close' + '-' * 20)


application = tornado.web.Application(
    handlers=[
        (r'/login', LoginHandler),
        (r'/index', IndexHandler),
        (r'/websocket', MessageWSHandler),
    ],
    template_path='template',
    login_url='/login',
    cookie_secret='R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM=',
    static_path='statics',
    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
        },
    },
    debug=True
)

if __name__ == '__main__':
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()

0001in_out.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% if error %}
        {{ error }}
    {% end %}
    <form method="post" action="/login?next={{ nextname }}">
        <p>用户:<input type="text" name="name"></p>
        <p>密码:<input type="password" name="password"></p>
        <input type="submit">
    </form>
</body>
</html>

04webscoket客户端编程

客户端已经解决,那么在客户端该怎么做呢?

 WebSocket

 WebSocket 是 HTML5 的标准之一,因此主流浏览器的 web 客户端编程语言 Javascript 已经支持 WebSocket 的客户端编程 

初始化 WebSocket 对象

var socket = new WebSocket(url):

处理函数


WebSocket.onopen : 此事件发生在 WebSocket 链接建立时

WebSocket.onmessage : 此事件发生在收到了来自服务器的消息时

WebSocket.onclose : 此事件发生在与服务器的链接关闭时

WebSocket.onerror : 此事件发生在通信过程中有任何错误时

主动操作函数

WebSocket.send(data) :向服务器发送消息

WebSocket.close() :主动关闭现有链接

代码

08websocket.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> WebSocket </title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
    <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 src="{{ static_url('js/jquery-2.2.0.min.js') }}"></script>


<script type="text/javascript">

    var mes = document.getElementById('messages');
    if ("WebSocket" in window) {
        mes.innerHTML = "发送WebSocket请求成功!";
        var ws = new WebSocket("ws://127.0.0.1:8000/websocket");

        ws.onopen = function () {
            alert('连接已打开请聊天')
        };

        ws.onmessage = function (goudan) {

            var received_msg = goudan.data;

            var aLi = $("<li>" + received_msg + "</li>");
            $(mes).append($(aLi)) //  方法一
//            $(aLi).appendTo(mes); //  方法二
//            $(mes).prepend($(aLi)) //消息在上面更新
        };

        ws.onclose = function () {
            mes.innerHTML = mes.innerHTML + "<br>连接已经关闭...";
        };
    }
    else {
        mes.innerHTML = "发送WebSocket请求失败!"
    }

    function WebSocketSend() {
        ws.send($("#text").val());
        $("#text").val("")  // 发完消息自动清空
    }
</script>

</body>
</html>

猜你喜欢

转载自blog.csdn.net/qq_14993591/article/details/82527294