最近学了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)中才可),所以本次效果展示是在本地进行展示