美文网首页
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