冬牧70黑麦草千粒重多少克?

小说:冬牧70黑麦草千粒重多少克?作者:辛王道石更新时间:2019-05-22字数:37280

而堕落龙魂则是被这铜钟直接伤及到了他的灵魂,使他受到了重创,才得以被叶扬捡了一个漏。

行情回暖,四季玫瑰苗价格今春预计稳中有涨

吞噬一棵树妖,就拥有这么滂湃的能量,要是将树妖森林的树妖全都吞噬了,自己还不晋级天境,甚至是超越天境了?
变态凶手这件事过去了,小萃虽然受到惊吓,但在雪飞鸿的保护下,她安然无恙,毫无损。

在叶扬将股票全部卖出去之后,他账户中的资金多了整整十万块钱,变成了二十万元,这着实让他兴奋了好大一会。

上一篇简单的实现了一个聊天网页,但这个太简单,消息全广播,没有用户认证和已读未读处理,主要的意义是走通了websocket-sharp做服务端的可能性。那么一个完整的IM还需要实现哪些部分?

一、发消息

用户A想要发给用户B,首先是将消息推送到服务器,服务器将拿到的toid和内容包装成一个完整的message对象,分别推送给客户B和客户A。为什么也要推送给A呢,因为A也需要知道是否推送成功,以及拿到了messageId可以用来做后面的已读未读功能。

这里有两个问题还要解决,第一个是Server如何推送到客户B,另外一个问题是群消息如何处理?

实现推送

先解决第一个问题,在Server端,每次连接都会创建一个WebSocketBehavior对象,每个WebSocketBehavior都有一个唯一的Id,如果用户在线我们就可以推送过去:

 Sessions.SendTo(userKey, Json.JsonParser.Serialize(msg));

需要解决的是需要将用户的Id和WebSocketBehavior的Id关联起来,所以这就要求每个用户连接之后需要马上验证。所以用户的流程如下:

由于JavaScript和Server交互的主要途径就是onmessage方法,暂时不能像socketio那样可以自定义事件让后台执行完成后就触发,我们先只能约定消息类型来实现验证和聊天的区分。

 function send(obj) {
        //必须是对象,还有约定的类型
        ws.send(JSON.stringify(obj))
    }
 socketSDK.sendTo = function (toId,msg) {
        var obj = {
            toId:toId,
            content: msg,
            type: "002"//聊天
        }
        send(obj);
      }
    socketSDK.validToken = function (token) {
          var obj = {
              content: token || localStorage.token,
              type: "001"//验证
          }
          send(obj);
      }

在后端拿到token就可以将用户的guid存下来,所有用户的guid与WebSocketBehavior的Id关系都保存在缓存里面。

var infos = _userService.DecryptToken(token);
 UserGuid = infos[0];
if (!cacheManager.IsSet(infos[0]))
  {
    cacheManager.Set(infos[0], Id, 60);
  }
//告之client验证结果,并把guid发过去
SendToSelf("token验证成功");

调用WebSocketBehavior的Send方法可以将对象直接发送给与其连接的客户端。接下来我们只需要判断toid这个用户在缓存里面,我们就能把消息推送给他。如果不在线,就直接保存消息。

群消息

群是一个用户的集合,发一条消息到群里面,数据库也只需要存储一条,而不是每个人都存一条,但每个人都会收到一次推送。这是我的Message对象和Group对象。

 public class Message
    {
       private string _receiverId;

       public Message()
       {
           SendTime = DateTime.Now;
           MsgId = Guid.NewGuid().ToString().Replace("-", "");
       }

       [Key]
       public string MsgId { get; set; }
       public string SenderId { get; set; }
       public string Content { get; set; }
       public DateTime SendTime { get; set; }
       public bool IsRead { get; set; }

       public string ReceiverId
       {
           get
           {
               return _receiverId;
           }
           set
           {
               _receiverId = value;
               IsGroup=isGroup(_receiverId);
           }
       }

       [NotMapped]
       public Int32 MsgIndex { get; set; }
       
       [NotMapped]
       public bool IsGroup { get; set; }

       public static bool isGroup(string key)
       {
           return !string.IsNullOrEmpty(key) && key.Length == 20;
       }
    }
View Code
 public class Group
    {
        private ICollection<User.User> _users;

        public Group()
        {
            Id = Encrypt.GenerateOrderNumber();
            CreateTime=DateTime.Now;
            ModifyTime=DateTime.Now;
        }

        [Key]
        public string Id { get; set; }
        public DateTime CreateTime { get; set; }
        public DateTime ModifyTime { get; set; }
 
       public string GroupName { get; set; }
       public string Image { get; set; }

       [Required]
       //群主
       public int CreateUserId { get; set; }
   
        [NotMapped]
        public virtual User.User Owner { get; set; }

        public ICollection<User.User> Users
        {
            get { return _users??(_users=new List<User.User>()); }
            set { _users = value; }
        }

        public string Description { get; set; }
       public bool IsDeleteD { get; set; }
    }
View Code

对于Message而言,主要就是SenderId,Content和ReceiverId,我通过ReceiverId来区分这条消息是发给个人的消息还是群消息。对于群Id是一个长度固定的字符串区别于用户的GUID。这样就可以实现群消息和个人消息的推送了:

            case "002"://正常聊天
                        //先检查是否合法
                        if (!IsValid)
                        {
                            SendToSelf("请先验证!","002");
                            break;
                        }
                        //在这里创建消息 避免群消息的时候多次创建
                        var msg = new Message()
                        {
                            SenderId = UserGuid,
                            Content = obj.content,
                            IsRead = false,
                            ReceiverId = toid,
                        };
                        //先发送给自己 两个作用 1告知对方服务端已经收到消息 2 用于对方通过msgid查询已读未读
                        SendToSelf(msg);

                        //判断toid是user还是 group
                        if (msg.IsGroup)
                        {
                            log("群消息:"+obj.content+",发送者:"+UserGuid);
                            //那么要找出这个group的所有用户
                            var group = _userService.GetGroup(toid);
                            foreach (var user in group.Users)
                            {
                                //除了发消息的本人
                                //群里的其他人都要收到消息
                                if (user.UserGuid.ToString() != UserGuid)
                                {
                                    SendToUser(user.UserGuid.ToString(), msg);
                                }
                            }
                        }
                        else
                        {
                            log("单消息:" + obj.content + ",发送者:" + UserGuid);
                            SendToUser(toid, msg);
                        }
                        //save message
                        //_msgService.Insert(msg);
                        break;

而SendToUser就可以将之前的缓存Id拿出来了。

 private void SendToUser(string toId, Message msg)
        {
            var userKey = cacheManager.Get<string>(toId);
            //这个判断可以拿掉 不存在的用户肯定不在线
            //var touser = _userService.GetUserByGuid(obj.toId);
            if (userKey != null)
            {
                //发送给对方
                Sessions.SendTo(userKey, Json.JsonParser.Serialize(msg));
            }
            else
            {
                //不需要通知对方
                //SendToSelf(toId + "还未上线!");
            }
        }

二、收消息

收消息包含两个部分,一个是发送回执,一个是页面消息显示。回执用来做已读未读。显示的问题在于,有历史消息,有当前的消息有未读的消息,不同人发的不同消息,怎么呈现呢?先说回执

回执

我定义的回执如下:

public class Receipt
    {
       public Receipt()
       {
           CreateTime = DateTime.Now;
           ReceiptId = Guid.NewGuid().ToString().Replace("-", "");
       }
       [Key]
       public string ReceiptId { get; set; }
       public string MsgId { get; set; }
       /// <summary>
       /// user的guid
       /// </summary>
       public string UserId { get; set; }
       public DateTime CreateTime { get; set; }
    }

回执不同于消息对象,不需要考虑是否是群的,回执都是发送到个人的,单聊的时候这个很好理解,A发给B,B读了之后发个回执给A,A就知道B已读了。那么A发到群里一条消息,读了这条消息的人都把回执推送给A。A就可以知道哪些人读了哪些人未读。

js的方法里面我传了一个toid,本质上是可以通过message对象查到用户的id的。但我不想让后端去查询这个id,前端拿又很轻松。

   //这个toid是应该可以省略的,因为可以通过msgId去获取
    //目前这么做的理由就是避免服务端进行一次查询。
    //toId必须是userId 也就是对应的sender
      socketSDK.sendReceipt = function (toId, msgId) {var obj= {
              toId: toId,
              content: msgId,
              type:"003"
          }
          send(obj)
      }
            case "003":
                        key = cacheManager.Get<string>(toid);
                        var recepit = new Receipt()
                        {
                            MsgId = obj.content,
                            UserId = UserGuid,
                        };
                        //发送给 发回执的人,告知服务端已经收到他的回执
                        SendToSelf(recepit);
                        if (key != null)
                        {
                            //发送给对方
                           await Sessions.SendTo(key, Json.JsonParser.Serialize(recepit));
                        }
// save recepit
                        break;

这样前端拿到回执就能处理已读未读的效果了。

消息呈现:

我采用的是每个对话对应一个div,这样切换自然,不用每次都要渲染。

当用户点击左边栏的时候,就会在右侧插入一个.messages的div。包括当收到了消息还没有页面的时候,也需要创建页面。 

 function leftsay(boxid, content, msgid) {
        //这个view不一定打开了。
        $box = $("#" + boxid);
        //可以先放到隐藏的页面上去,
        word = $("<div class="msgcontent">").html(content);
        warp = $("<div class="leftsay">").attr("id", msgid).append(word);
        if ($box.length != 0) {
            $box.append(warp);
        } else {
            $box = $("<div class="messages" id=" + boxid + ">");
            $box.append(word);
            $("#messagesbox").append($box);    
        }
    }

未读消息

当前页面不在active状态,就不能发已读回执。

   
 function unreadmark(friendId, count) {
        $("#" + friendId).find("span").remove();
        if (count == 0) {
            return;
        }
        var span = $("<span class="unreadnum" >").html(count);
        $("#"+friendId).append(span);
    }

sdk.on("messages", function (data) {
        if (sdk.isSelf(data.senderid)) {
            //自己说的
            //肯定是当前对话
            //照理说还要判断是不是当前的对话框
            data.list = [];//为msg对象增加一个数组 用来存储回执
            if (data.isgroup)
            selfgroupmsg[data.msgid] = data;//缓存群消息 用于处理回执
            rightsay(data.content, data.msgid);
        } else {
            //别人说的
            //不一定是当前对话,就要从ReceiverId判断。
            var _toid = data.senderid;
            if (!sdk.isSelf(data.receiverid)) {
                //接受者不是自己 说明是群消息
                _toid = data.receiverid;
            }
            var boxid = _toid + viewkey;

            //如果是当前会话就发送已读回执
            if (_toid == currentToId) {
                sdk.sendReceipt(data.senderid, data.msgid);
            } else {
                if (!msgscache[_toid]) {
                    msgscache[_toid] = [];
                }
                //存入未读列表
                msgscache[_toid].push(data);
                unreadmark(_toid, msgscache[_toid].length);
            }

            leftsay(boxid, data.content, data.msgid);

        }

    });

单聊的时候已读未读比较简单,就判断这条消息是否收到了回执。

 $("#" + msgid).find(".unread").html("已读").addClass("ed");

但是群聊的时候,显示的是“几人未读”,而且要能够看到哪些人读了哪些人未读,为了最大的减少查询,在最初获取联系人列表的时候就需要将群的成员也一起带出来,然后前端记录下每一条群消息的所收到的回执。这样每收到一条就一个人。而前端只需要缓存发送的群消息即可。

 function readmsg(data) {
        //区分是单聊还是群聊
        //单聊就直接是已读
        var msgid = data.msgid;
        var rawmsg = selfgroupmsg[msgid];
        if (!rawmsg) {
            $("#" + msgid).find(".unread").html("已读").addClass("ed");
        }
        else {
            rawmsg.list.push(data);
            //得到了这个群的信息
            var ginfo = groupinfo[rawmsg.receiverid];
            //总的人数
            var total = ginfo.Users.length;
            //找到原始的消息
            //已读的人数
            var readcount = rawmsg.list.length;
            //未读人数
            var unread = total - readcount-1;//除去自己
            var txt = "已读";
            if (unread != 0) {
                txt = unread + "人未读";
                $("#" + msgid).find(".unread").html(txt);
            } else {
                $("#" + msgid).find(".unread").html(txt).addClass("ed");
            }
        }
    }

这样就可以显示几人未读了:

小结:大致的流程已经走通,但还有些问题,比如历史消息和消息存储还没有处理,文件发送,另外还有对于一个用户他可能不止一个端,要实现多屏同步,这就需要缓存下每个用户所有的WebSocketBehavior对象Id。 后续继续完善。

 

编辑:安公

发布:2019-05-22 03:55:19

当前文章:http://adsl66.com/ojscx.html

八角金盘冬季落叶吗? 3.5公分紫竹哪家报价最低? 3.5公分刚竹哪里种植基地便宜? 新疆樱花价格便宜吗? 紫藤栽培最佳时间是什么时候? 紫藤花在哪个季节盛开? 3公分国槐价格 上车价22元 你一定不知的国内最大的红叶碧桃基地就在江苏沭阳吧!

41324 20334 72159 42413 84196 96123 61006 75787 29525 79154 75689 50846 81311 84742 20859 56597 87553 12372 19044 34577

我要说两句: (0人参与)

发布