首页 > 技术文章 > tornado-websocket

tangpg 2018-08-20 15:37 原文

WebSockets 允许浏览器和服务器之间进行 双向通信

 server端:

class WebSocketHandler(WebBaseHandler):
    ''' websocket '''
    users = {} # {u'liubei': <handlers.message.message_handler.WebSocketHandler object at 0xb620b34c>,
               #  u'rock': <handlers.message.message_handler.WebSocketHandler object at 0xb61794ec>}

    # ------------------提高部分 开始------------------
   # 提高部分只是细分发送信息的对象,不是很重要。完成提高部分结束后的 open,on_message, on_close即可。
@classmethod def send_system_message(cls, self, content, send_type): """ :param self: 继承过websocket的base类的实例化对象,主要是self初始化了操作redis,mysql以及user对象的属性 :param content: 储存进redis的list data :param send_type: 发送的类型(发给boss,ceo,员工...),由前端传过来 :return: 系统消息,发送给每个人 """ target = 'system' redis_msg = cls.dict_to_json(self, content, send_type, target) self.conn.rpush('message:%s' % send_type, redis_msg) for f, v in WebSocketHandler.users.items(): v.write_message(redis_msg) @classmethod def dict_to_json(cls, self, content, send_type, target): """ :param self: 继承过websocket的base类的实例化对象,主要是self初始化了操作redis,mysql以及user对象的属性 :param content: 储存进redis的list data :param send_type: 发送的类型(发给boss,ceo,员工...),由前端传过来 :param target: 相当与用来区分缓存key的名字,比如 key_name="cache_list:%s"%target1, ...取对应分类的历史数据 :return: """ msg = { "content": content, "send_type": send_type, "sender": self.current_user.name, "target": target, "datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } return tornado.escape.json_encode(msg) @classmethod def send_role_message(cls, self, content, send_type, roleid): """ :param self: :param content: :param send_type: :param roleid: 通过角色id,反查出属于该角色id的用户 :return: 发送信息给 该角色的所用用户 """ role = Role.by_id(roleid) redis_msg = cls.dict_to_json(self, content, send_type, role.name) self.conn.rpush('message:%s' % send_type, redis_msg) role_users = role.users # [zhangsan, lishi , wangwu] [zhangsan, lishi] for user in role_users: if WebSocketHandler.users.get(user.name, None) is not None: # user.name ['rock':self] WebSocketHandler.users[user.name].write_message(redis_msg) else: # self.conn.lpush("ws:role_off_line",message) pass @classmethod def send_user_message(cls, self, content, send_type, user): """ :param self: :param content: :param send_type: :param user: 发送的对象 :return: 发送信息给改用户 user """ redis_msg = cls.dict_to_json(self, content, send_type, user) self.conn.rpush('message:%s' % send_type, redis_msg) self.conn.rpush('message:%s' % user, redis_msg) # 为了显示未读消息条数 if cls.users.get(user, None) is not None: cls.users[user].write_message(redis_msg) else: # self.conn.lpush("ws:user_off_line",message) pass # ------------------提高部分 结束------------------ def open(self): ''' 有用户进来后,存储该用户 {usernaem: self} self是每个登录用户的实例化类 (用该实例化对象发送是那个消息) ''' WebSocketHandler.users[self.current_user.name] = self pass def on_message(self, message): # 改方法获取消息,前端通过 ws对象.send(msg)发送 # print message # {"content_html":"聊天框输入的内容"} json # {"content_html":"afaf<img src=\"/static/images/face/nm_thumb.gif\" title=\"[怒骂]\">"} msg = tornado.escape.json_decode(message) # 解码 json字符串 --> 字符串 msg.update({ "name": self.current_user.name, "datetime": datetime.now().strftime("%Y-%m-%d %H-%M-%S") }) message = tornado.escape.json_encode(msg) # 转成json self.conn.rpush('message:list', message) # 存储消息为了显示历史消息 # self.write_message(msg) # 就算不转成json,write_message也能自己编码 # WebSocketHandler.users['liubei'].write_message(message) # 这是将不管谁的message只发给用户liubei for f, v in WebSocketHandler.users.items(): v.write_message(message) def on_close(self): pass

前端:和后端一样,都需要完成open,on_message,on_close三个方法

 

<script type="text/javascript">
    $(document).ready(function(){
            //与服务器建立websocket链接请求
            var url="ws://" + location.host + "/ws";  //是open, on_message, on_close所在类映射的路由。是通过ws协议,而不是http 
            var ws= new WebSocket(url);//在浏览器打开一个socket  服务器打开一个socket

            ws.onopen=function(){
                $('#status').text('已经建立链接');
                var tishi = $('.tishi');
                var name = tishi.attr('username');
                tishi.append("<div>"+name+"加入了聊天室...</div>")
            };

            ws.onclose=function () {
                $('#status').text('已经断开链接')
            };

            //当收到服务器向浏览器推送消息时调用这个函数
            ws.onmessage=function(event){

                message = JSON.parse(event.data);
              if(message.content_html){
                  append(message);
              }else{
                  append_m(message);
              }
            };


            //点出头像函数
            $('.t_gif').click(function(){
                $('.t_box').toggle(300);
            });

            // 点击表情时把点击的表情添加到文本框中
            $('#q_ul li').click(function(){
                var img = $(this).find("img").clone();
                $(".t_input").append(img);
                $(".t_input").focus();
            });

            // 点击发布按钮时调用wsbsocket.send()函数向服务器发送数据
            $(".t_btn").click(function(){
                var content_html = $('.t_input').html();
                console.log(content_html);
                var massage = {
                    "content_html":content_html
                };
                ws.send(JSON.stringify(massage));

            });

            // 动态添加发布消息的函数
            function append(msg){
                //向留言中添加消息
                $(".t_all").prepend(function(n){
                    var content_html='';
                    var useravatar='';
                    var datetime1='';
                    if(msg){
                        content_html = msg.content_html;
                        var useravatar = 'defaut_avatar.jpeg';
                        datetime1 = msg.datetime;
                    }
                    return "<div class='t_list animated bounceIn'>"+
                                "<div class='t_header'>"+
                                    "<img src='/static/images/useravatars/" + useravatar +"' alt='' width='64' height='64' />"+
                                "</div>"+
                                "<div class='t_icon'></div>"+
                                "<div class='t_msg'>"+"<p style='font-size:8px;'>"+
                                "<a class='name' href='#'>" +msg.name+ "&nbsp;&nbsp;&nbsp;</a>"+
                                    datetime1+"</p>"+content_html+"</div>"+
                                "<div class='clear'></div>"+
                            "</div>"
                    });
                $('.t_box').hide(400);
                $('.t_input').text('');
                $('.t_input').focus();
            }


            // 发布系统消息的函数
            function append_m(msg){
                $(".system_all").html('');
                var target = "";
                if(msg.target == "system"){
                    target = "全体人员"
                }else{
                    target = msg.target
                }
                var messages = "消息内容:"+msg.content+
                    "&nbsp;&nbsp;&nbsp;消息类型:"+
                    msg.send_type+"&nbsp;&nbsp;&nbsp;发送者:"+
                    msg.sender +"&nbsp;&nbsp;&nbsp;接收者:"+
                    target+"&nbsp;&nbsp;&nbsp;时间:"+
                    msg.datetime;
                $(".system_all").html(messages);
            }
    })
</script>

 

 

参考中文文档:https://tornado-zh.readthedocs.io/zh/latest/websocket

 

推荐阅读