美文网首页
tornado 聊天室

tornado 聊天室

作者: 遥远的她197 | 来源:发表于2019-03-16 17:10 被阅读0次
    image.png

    在manage.py页面中

    import os
    
    import tornado.web
    import tornado.ioloop
    from tornado.options import define, options, parse_command_line
    
    from app.views import LoginHandler, ChatHandler
    
    define('port', default=8080, type=int)
    
    
    def make_app():
        return tornado.web.Application(handlers=[
            (r'/login/', LoginHandler),
            (r'/chat/', ChatHandler),
        ],
        template_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates'),
        static_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
        )
        # cookie_secret = 'dwdqddqdqd' 要用安全的cookie就放在static_path的后面
    
    
    if __name__ == '__main__':
        parse_command_line()
        app = make_app()
        app.listen(options.port)
        tornado.ioloop.IOLoop.current().start()
    

    在async_tornado.py

    import tornado.web
    import tornado.ioloop
    import tornado.httpclient
    
    
    class IndexHandler(tornado.web.RequestHandler):
        # 这步是连接不会关闭
        @tornado.web.asynchronous
        def get(self):
            q = self.get_argument('q')
            client = tornado.httpclient.AsyncHTTPClient()
            # 这里想访问要卡着,所以我们要连接不能断开才有@tornado这步
            client.fetch('https://cn.bing.com/search?q=%s' % q, callback=self.on_response)
    
            self.write('异步测试')
    
        # 回调, 当页面响应,则调用回调函数on_response
        def on_response(self, response):
            print(response)
            self.write('回调执行')
            # 完成响应了之后, 就可以关闭连接了
            self.finish()
    
    
    def make_app():
        return tornado.web.Application(handlers=[
            (r'/index/', IndexHandler)
        ])
    
    
    if __name__ == '__main__':
        app = make_app()
        app.listen(8080)
        tornado.ioloop.IOLoop.current().start()
    

    在async_tornado2.py中

    import tornado.web
    import tornado.ioloop
    import tornado.httpclient
    
    
    # 异步变同步(优化代码),它还是异步
    class IndexHandler(tornado.web.RequestHandler):
        # 这步是让连接不会关闭
        @tornado.web.asynchronous
        @tornado.web.gen.coroutine
        def get(self):
            q = self.get_argument('q')
            client = tornado.httpclient.AsyncHTTPClient()
            # 这里想访问要卡着,所以我们要连接不能断开才有@tornado这步
            response = yield client.fetch('https://cn.bing.com/search?q=%s' % q)
            print(response)
            self.write('异步测试')
    
        # 改为同步了(优化了), 下面就不需要了
        # 回调, 当页面响应,则调用回调函数on_response
        # def on_response(self, response):
        #     print(response)
        #     self.write('回调执行')
        #     # 完成响应了之后, 就可以关闭连接了
        #     self.finish()
    
    
    def make_app():
        return tornado.web.Application(handlers=[
            (r'/index/', IndexHandler)
        ])
    
    
    if __name__ == '__main__':
        app = make_app()
        app.listen(8080)
        tornado.ioloop.IOLoop.current().start()
    

    在sync_tornado.py中

    import tornado.web
    import tornado.ioloop
    import tornado.httpclient
    
    
    class IndexHandler(tornado.web.RequestHandler):
        def get(self):
            # 路由中传递的参数, /index/?q=python
            q = self.get_argument('q')
            # 向地址发送请求: https://cn.bing.com/search?q=
            client = tornado.httpclient.HTTPClient()
            # 因为是同步的,它必须等着第一个请求完成才能,所以不需要连接 -- @tornado
            response = client.fetch('https://cn.bing.com/search?q=%s' % q)
            print(response)
            self.write('同步测试')
    
    
    def make_app():
    
        return tornado.web.Application(handlers=[
            (r'/index/', IndexHandler)
        ])
    
    
    if __name__ == '__main__':
        app = make_app()
        app.listen(8000)
        tornado.ioloop.IOLoop.current().start()
    

    app文件中的视图文件

    在views.py中

    import tornado.web
    import tornado.websocket
    
    
    class LoginHandler(tornado.web.RequestHandler):
    
        def get(self):
            error = ''
            self.render('login.html', error=error)
    
        def post(self):
            # 获取登陆用户信息
            username = self.get_argument('username')
            # self.get_body_argument()  # 和上面一样
            password = self.get_argument('password')
            # 模拟登录
            if username in ['coco', 'vincent'] and password == '123456':
                # self.set_secure_cookie() 设置安全的cookie值 要在manage.py那里设置加密
                self.set_cookie('username', username, expires_days=1)
                self.render('chat.html', username=username)
                # 在chat.html(聊天页面)建立连接
            else:
                error = '账号或者密码错误'
                self.render('login.html', error=error)
    
    
    class ChatHandler(tornado.websocket.WebSocketHandler):
        # 用于存放连接的对象
        user_online = []
    
        # self是和客户端连接的对象
        # 当进入chat.html页面时,会主动触发该函数, websocket调用这个方法
        def open(self, *args, **kwargs):
            self.user_online.append(self)
            for user in self.user_online:
                # self.get_secure_cookie() # 安全的cookie
                username = self.get_cookie('username')
                user.write_message('系统提示:【%s已经入聊天室】' % username)
    
        def on_message(self, message):
            # 接收前端传的数据
            username = self.get_cookie('username')
            for user in self.user_online:
                user.write_message('%s:%s' % (username, message))
    
        def on_close(self):
            # 移除连接对象
            self.user_online.remove(self)
            for user in self.user_online:
                username = self.get_cookie('username')
                user.write_message('系统提示:【%s已退出聊天室】' % username)
    

    在静态文件templates包中

    1.chat.html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>聊天室</title>
    
        <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    </head>
    <body>
          <p>当前账号: {{ username }}</p>
          <div id="chat" style="width:300px; height:300px; border:1px solid #000000;">
              <!--聊天窗口-->
    
          </div>
             <!--输入信息窗口-->
          <input type="text" name="content" id="content">
          <input type="button" id="btn" value="提交">
    
          <script>
             <!--建立个链接 ws:建立连接的地址-->
              var websocket = new WebSocket('ws://127.0.0.1:8080/chat/')
             <!--获取后端返回的数据(onmessage接收数据)-->
             websocket.onmessage = function(e){
                <!--获取输入的数据, data就是值,所以用e.data取值-->
                console.log(e.data)
                <!--在聊天框里插入数据(系统提示:你好)-->
                <!--var chat = document.getElementById('chat');-->
                <!--chat.innerText = e.data 这两部和下面一样-->
                $('#chat').append('</br>')
                $('#chat').append(e.data)
             }
    
             <!--提交输入的内容-->
             $('#btn').click(function(){
                <!--向后端发送数据-->
                var content = $('#content').val()
                websocket.send(content)
             });
          </script>
    </body>
    </html>
    

    2.login.html文件中

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
        <p>登录聊天室界面</p>
           {{ error }}
        <form action="" method="post">
            <p>用户名: <input type="text" name="username"></p>
            <p>密码: <input type="text" name="password"></p>
            <p><input type="submit" value="提交"></p>
        </form>
    </body>
    </html>
    

    style里目前是空的,你可以装饰聊天室

    --------------------------------

    1. WebSocket

    Tornado中也支持WebSocket,模块名为tornado.websocket,其中提供了一个WebSocketHandler类进行处理通信。

    WebSocketHandler类相关方法如下:

    1. open() :表示当一个WebSocket连接建立后,会主动调用。

    2. on_message(message) : 表示接收客户端发送过来的message参数。

    3. on_close() : 表示关闭WebSocket连接后被调用。

    4. write_message(message,binary=False) : 表示向客户端发送消息message,binary参数为True表示发送任何字节码,binary为False表示咦utf8编码发送message。

    5. close() : 关闭WebSocket连接。

    6. check_origin(origin) : 表示判断源origin,如果源origin符合条件则返回True,如果不符合则返回403。重写该方法用以解决跨域请求。即返回True即可。

    2. 多人在线聊天室(利用WebSocket)

    image.png

    Tornado聊天室应用项目结构图中各模块的用法:

    views.py文件: 编写业务逻辑的py文件。
    static文件夹: 用于存放静态文件的文件夹。
    templates文件夹: 用于存放模板的文件夹。
    settings.py文件: 用于定义配置信息的py文件。
    manage.py文件: 用于启动项目的py文件。
    
    

    2.1 启动manage.py文件

    manage.py文件作为启动项目文件,因此py文件主要实现配置路由、静态资源、监听端口等功能

    import tornado.web
    import tornado.httpserver
    import tornado.ioloop
    
    from tornado.options import define, options
    
    from chat.views import IndexHandler, LoginHandler, ChatHandler
    from utils.settings import TEMPLATE_PATH, STATIC_PATH
    
    define("port", default=8180, help="run on the given port", type=int)
    
    def make_app():
        return tornado.web.Application(handlers=[
            (r'/', IndexHandler),
            (r'/login', LoginHandler),
            (r'/chat', ChatHandler),
        ],
            template_path=TEMPLATE_PATH,
            static_path=STATIC_PATH,
            debug=True,
            cookie_secret='cqVJzSSjQgWzKtpHMd4NaSeEa6yTy0qRicyeUDIMSjo='
        )
    
    # 程序运行入口
    if __name__ == '__main__':
        app=make_app()
        http_server=tornado.httpserver.HTTPServer(app)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.current().start()
    
    

    2.2 定义项目的配置settings.py文件

    import os
    
    # 获取项目的绝对路径
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    # 静态文件
    TEMPLATE_PATH = os.path.join(BASE_DIR, 'templates')
    STATIC_PATH = os.path.join(BASE_DIR, 'static')
    
    

    2.3 定义views.py文件

    import tornado.web
    import tornado.websocket
    
    class IndexHandler(tornado.web.RequestHandler):
        # 定义首页视图处理类,提示用户登录
        def get(self):
            self.render('index.html')
    
    class LoginHandler(tornado.web.RequestHandler):
        # 定义登录视图处理类
        def get(self):
            # 获取用户登录的昵称
            nickname=self.get_argument('nickname')
            # 将用户登录的昵称保存在cookie中,安全cookie
            self.set_secure_cookie('nickname',nickname)
            self.render('chat.html',nickname=nickname)
    
    class ChatHandler(tornado.websocket.WebSocketHandler):
        # 定义接收/发送聊天消息的视图处理类,继承自websocket的WebSocketHandler
    
        # 定义一个集合,用来保存在线的所有用户
        online_users = set()
        # 从客户端获取cookie信息
    
        # 重写open方法,当有新的聊天用户进入的时候自动触发该函数
        def open(self):
            # 当有新的用户上线,将该用户加入集合中
            self.online_users.add(self)
            # 将新用户加入的信息发送给所有的在线用户
            for user in self.online_users:
                user.write_message('【%s】进入了聊天室' % self.request.remote_ip)
    
        # 重写on_message方法,当聊天消息有更新时自动触发的函数
        def on_message(self, message):
            # 将在线用户发送的消息通过服务器转发给所有的在线用户
            for user in self.online_users:
                user.write_message('%s:%s' % (self.request.remote_ip, message))
    
        # 重写on_close方法,当有用户离开时自动触发的函数
        def on_close(self):
            # 先将用户从列表中移除
            self.online_users.remove(self)
            # 将该用户离开的消息发送给所有在线的用户
            for user in self.online_users:
                user.write_message('【%s】离开了聊天室~' % self.request.remote_ip)
    
        # 重写check_origin方法, 解决WebSocket的跨域请求
        def check_origin(self, origin):
            return True
    
    

    2.4 定义index.html页面和chat.html页面

    index.html页面代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>聊天室登录首页</title>
        <script src="/static/js/jquery.js"></script>
    </head>
    <body>
    <div>
        <div style="width:60%;">
            <div>
                聊天室个人登录
            </div>
            <div>
                <form method="get" action="/login" style="width:80%">
                    <p>昵称:<input type="text" placeholder="请输入昵称" name="nickname"></p>
                    <button type="submit">登录</button>
                </form>
            </div>
        </div>
    </div>
    </body>
    </html>
    
    

    chat.html页面代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Tornado聊天室</title>
        <script src="/static/js/jquery.js"></script>
    </head>
    <body>
    <div>
        <div style="font-weight:bold; font-size:2em;">
            聊天室
        </div>
        <div class="content">
            <div class="receive">
    
            </div>
            <div class="send">
                <textarea id="send_content"> </textarea>
                <br>
                <button  id="btn" >发送</button>
            </div>
        </div>
    </div>
    <script>
        // 创建一个websocket长连接对象
        var _websocket= new WebSocket('ws://127.0.0.1:8180/chat')
        //发送消息
        $('#btn').click(function(){
            //获取消息内容
            $msg=$('#send_content').val();
            //发送消息
            _websocket.send($msg);
             $("#msg").val('');
        })
    
        //接收消息,当消息更新时自动触发
        _websocket.onmessage=function(e){
            console.log(e)
            var $content=e.data;
    
            //重构date的Format属性
            Date.prototype.Format = function (fmt) {
                var o = {
                    "M+": this.getMonth() + 1, //月份
                    "d+": this.getDate(), //日
                    "H+": this.getHours(), //小时
                    "m+": this.getMinutes(), //分
                    "s+": this.getSeconds(), //秒
                    "q+": Math.floor((this.getMonth() + 3) / 3), //季度
                    "S": this.getMilliseconds() //毫秒
                };
                if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
                for (var k in o)
                if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
                return fmt;
                }
    
                var date=new Date();
                var $time=date.Format("yyyy年MM月dd HH:mm:ss");
    
                // 添加内容到class为recceive的div框中
                var $p1=$('<p style="color:red;">').text($content);
                var $p2=$('<p style="color:#000000;">').text($time);
                $('.receive').append($p2)
                $('.receive').append($p1)
        }
    </script>
    </body>
    </html>
    
    

    启动Tornado应用项目, 即运行 'python manage.py',并开启多浏览器访问http://127.0.0.1:8180/地址,在个人登录页面中输入当前用户的昵称,即可跳转到聊天页面。

    相关文章

      网友评论

          本文标题:tornado 聊天室

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