zoukankan      html  css  js  c++  java
  • tornado+websocket+mongodb实现在线视屏文字聊天

    最近学了tornado和mongo,所以结合websocket 实现一个聊天功能,从而加深一下相关知识点的印象

    1.websocket概览

    webscoket是一种全双工通信模式的协议,客户端连接服务端先通过tcp,http转为webscoket协议后,客户端和服务端都可以主动推送消息给另一端,这也是和http协议(服务端只能被动接收消息,无法主动推送消息给客户端)最大的区别。

    2.tornado概览

    tornado是一种异步网络库的python web框架,最初在 FriendFeed上开发,通过使用非阻塞网络I/O,tornado可以扫描数以万计打开的链接,让它成为给每个用户一个长链接的理想选择。

    Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed. By using non-blocking network I/O, Tornado can scale to tens of thousands of open connections, making it ideal for long polling, WebSockets, and other applications that require a long-lived connection to each user.

    3.实现在线聊天功能概览

    • 判断对方是否在线,保存离线消息,在线时推送过去,并删除离线消息
    • 不保存在线消息
    • 单点登录模式的聊天
    • 同时和多人点对点聊天

     4.后端代码

    import redis
    import tornado.httpserver
    import tornado.ioloop
    import tornado.web
    from motor import motor_tornado
    from tornado import websocket
    from tornado.gen import coroutine
    
    from conf import Config
    
    
    class WebHandler(websocket.WebSocketHandler):
        # 在线用户dict
        all_user = {}
    
        def initialize(self, mongo, redis):
            self.mongo = mongo
            self.redis = redis
    
        @property
        def db(self):
            return self.mongo.get_database('rgc')
    
        @property
        def col(self):
            return self.db.get_collection('web')
    
        def check_origin(self, origin):
            return True  # 允许WebSocket的跨域请求
    
        @coroutine
        def on_message(self, message):
            #因为没有登录相关功能,每次传输都 用 # 拼接 发送者,消息,接受者
            resu = str.split(message, '#')
    
            name = resu[0]
            val = resu[1]
            to = resu[2]
            # 判断对方是否在线
            if 'name:{}'.format(to) not in list(self.all_user.keys()):
                self.write_message('Out Line')
                # 存储消息到db
                # if not self.redis.hget('name_list', '{}:{}'.format(name, to)):
                #     self.redis.hset('name_list', '{}:{}'.format(name, to), 1)
                self.col.insert({'name': '{}:{}'.format(name, to), 'msg': val})
            else:
                # 发送最新消息
                self.all_user['name:{}'.format(to)].write_message(val)
    
            # 检查是不是第一次上线
            if 'name:{}'.format(name) not in list(self.all_user.keys()):
                # 给自己发送历史消息
                his_one = self.col.find({'name': '{}:{}'.format(to, name)}, {'msg': 1, '_id': 0})
                for it in (yield his_one.to_list(100)):
                    self.write_message(it['msg'])
            # 删除历史消息
            self.col.delete_many({'name': '{}:{}'.format(to, name)})
            # 单点登录聊天
            self.all_user.update({'name:{}'.format(name): self})
            # 发给自己
            self.write_message('send success')
    
        def open(self):
            pass
    
        def on_close(self):
            # 当客户端关闭连接时,去除内存中保存的用户,让其离线
            key = None
            for k, v in self.all_user.items():
                if v == self:
                    key = k
                    break
            if key:
                self.all_user.pop(key)
            print('{} out line'.format(key))
    
    
    class HtmlHandler(tornado.web.RequestHandler):
        def get(self):
            self.render("static/index.html")
    
    
    class StaticHandler(tornado.web.RequestHandler):
        def get(self, file_url):
            self.render("static/{}".format(file_url))
    
    
    def make_app():
        settings = {'cookie_secret': 'dfdfdfd',
                    'xsrf_cookies': True,
                    'debug': True}
        other_db = {'mongo': motor_tornado.MotorClient(**Config.get('MONGO_CONF')),
                    'redis': redis.StrictRedis()}
        return tornado.web.Application([
            (r'/web', WebHandler, other_db),
            (r'/', HtmlHandler),
            (r'/static/(.*)', StaticHandler)
        ], **settings)
    
    
    if __name__ == '__main__':
        app = make_app()
        http_server = tornado.httpserver.HTTPServer(app)
        ip='127.0.0.1'
        port = 8000
        http_server.bind(8000, ip)
        http_server.start(1)
        print('server start! http://{}:{}'.format(ip, port))
        tornado.ioloop.IOLoop.current().start()

     5.前端技术:

    前端主要使用到了  MediaSource,Blob 等技术,发送视频大致思路为:先获取视频文件发送给服务端,然后从服务端发送给另一个客户端,客户端进行视频解析后播放出来。

    效果展示:(因为开启浏览器视频功能,必须在https环境或者本地回环地址(127.0.0.1)中才可),所以本次效果展示是在本地进行展示

    • 谷歌浏览器(因为我是后端开发,前端代码没有做兼容,并且界面有点丑)开启两个网页,输入  http://127.0.0.1:8000/
    • 进入界面后,在name里输入自己的姓名,message中输入 发送给对方的消息,to 里面输入 对方名字,然后点击发送

    • lucy发送工tom的信息,在发送信息时,系统检测到tom给她发了离线消息,所以一并返回给lucy。
    • 视频发送测试,name和to用的还是之前的:
    • 看一下 浏览器控制台 websocket接口的相关内容:

    • 可以看到所有的交流都是在一个 websocket连接中,双方可以互发消息

     项目代码所在地: https://github.com/Rgcsh/tornado_websocket

  • 相关阅读:
    自考毕业答辩总结
    【项目经验】navicat工具 SQLServer数据库迁移MySQL
    【项目经验】EasyUI Tree
    Django框架基础(一)
    前端相关内容补充
    web框架前戏---web框架的本质
    web框架前戏---基础简介及Django安装配置
    sqlAchemy前戏
    mysql基础(五)之pymysql
    mysql基础(四)之索引
  • 原文地址:https://www.cnblogs.com/rgcLOVEyaya/p/RGC_LOVE_YAYA_810days.html
Copyright © 2011-2022 走看看