zoukankan      html  css  js  c++  java
  • 实现过程全纪录——自己写一个“微信朋友圈”(包括移动端与PC端)

    一.朋友圈的基本单元——动态

          首先定义一个自定义控件用来显示每条动态。

    二.运行效果

          

    三.核心解读

          PushedMessage 有个PushIndex属性,表示发送消息的index,从0开始递增。每个人Push的index都是从0开始,ResponseMoments 的 MessageList 是个字典,Key表示 index。

            //
            // 摘要:
            //     拉取我的朋友们发的消息。当收到服务器回复,将触发ResponseMomentsReceived事件。
            //
            // 参数:
            //   startPullIndex:
            //     从哪个index的消息开始拉取(不包含该index对应的那条)。如果startPullIndex小于0,表示拉取最新的N条。
            //
            //   latest:
            //     latest为true,表示拉取更新的(大于startPullIndex);为false表示拉取之前的(小于startPullIndex)。
            void Pull(long startPullIndex, bool latest);
            //
            // 摘要:
            //     拉取目标用户所发的消息。当收到服务器回复,将触发ResponseMomentsReceived事件。
            //
            // 参数:
            //   ownerID:
            //     所要拉取的目标用户的ID
            //
            //   startPushIndex:
            //     从哪个index的消息开始拉取(不包含该index对应的那条)。如果startPushIndex小于0,表示拉取最新的N条。
            //
            //   latest:
            //     latest为true,表示拉取更新的(大于startPullIndex);为false表示拉取之前的(小于startPullIndex)。
            void Pull(string ownerID, long startPushIndex, bool latest);

          当调用这个方法时,表示获取我的朋友们的动态,我朋友们的动态会排序,index就是用这个key表示的。

          latest 参数 表示是下拉,还是上拉。 

    四.DynamicsLoader解读 

          DynamicsLoader中最主要的是Load方法。

      public void Load(Dictionary<long, PushedMessage> currentMessageList, FlowLayoutPanel flowLayoutPanel, IMomentsClient client)
            {
                this.preLastIndex = this.lastIndex;
                foreach (var item in currentMessageList)
                {
                    if (this.firstIndex < 0)
                    {
                        this.firstIndex = item.Key - currentMessageList.Count + 1;
                    }
                    if (item.Key < this.firstIndex)
                    {
                        this.firstIndex = item.Key;
                    }
                    if (item.Key > this.lastIndex)
                    {
                        this.lastIndex = item.Key;
                    }
                    if (!this.messageList.ContainsKey(item.Key))
                    {
                        this.messageList.Add(item.Key, item.Value);
                    }
                }
                this.freeIndex = this.lastIndex - 20;
    
                if ((this.lastIndex - this.preLastIndex) > 39)
                {
                    this.firstIndex = this.lastIndex - currentMessageList.Count + 1;
                    this.preLastIndex = this.firstIndex;
                    this.freeIndex = this.preLastIndex;
                    this.messageList.Clear();
                    this.messageList = currentMessageList;
                }
                this.listIndex = new List<long>((IEnumerable<long>)this.messageList.Keys);
                this.listIndex.Sort();
                flowLayoutPanel.Controls.Clear();           
                foreach (var item in this.listIndex)
                {
                    DynamicsControl crl = new DynamicsControl();
                    crl.Initialize(this.messageList[item].OwnerID, Encoding.UTF8.GetString(this.messageList[item].Content), this.messageList[item].PushTime);
                    flowLayoutPanel.Controls.Add(crl);
                    flowLayoutPanel.Controls.SetChildIndex(crl, 0);            
                }
                this.listIndex.Clear();
                this.listIndex = null;
                if (this.isUp)
                {
                    if (flowLayoutPanel.Controls.Count != 0)
                    {
                        flowLayoutPanel.ScrollControlIntoView(flowLayoutPanel.Controls[0]);
                    }              
                }
                else
                {
                    if (flowLayoutPanel.Controls.Count != 0)
                    {
                        flowLayoutPanel.ScrollControlIntoView(flowLayoutPanel.Controls[flowLayoutPanel.Controls.Count - 1]); 
                    }       
                }          
                if (currentMessageList.Count == 0)
                {
                    return;
                }
                if ((this.lastIndex - this.preLastIndex) < 20)
                {
                    return;
                }
                do
                {
                    client.Pull(this.freeIndex, false);
                    this.freeIndex -= 20;
                }
                while (this.freeIndex > this.preLastIndex);
            }

          正如前面所言,每一条动态都有一个index,那么我每次获取动态的时候,就需要保留一个最小的索引,用于下拉的时候使用;保留一个最大的索引,用于上拉的时候使用。当最新的动态比本地的动态多出20条以上时,则分为两种情况:1.两次可以加载完的就加载两次 ,2两次无法加载完的,就只加载最新的20条,并将本地旧动态全部删除。 

    五.移动端如何通信

    一.消息编解码总体策略  

    NPush默认使用紧凑的序列化器,其遵循的策略如下:
    字符串一律使用UTF-8编码。如果是基础数据类型,则直接记录其字节。
    如果是bool,则用一个字节表示,0表示false,1表示true。
    如果是string,先记录其长度(int,-1表示为null,0表示string.Empty),再记录UTF8编码的字节。
    如果是byte[],先记录其长度(int,-1表示为null),再记录其内容。
    如果是Image ,先记录其长度(int,-1表示为null),再记录是否为Gif(一个byte),再记录序列化内容。
    如果是Color ,长度固定为3个字节,一次记录R/G/B的值。
    如果是List<>,先记录元素数(int,-1表示为null),再依次记录每个元素的内容。
    如果是Dictionary<,>,先记录元素对的个数(int,-1表示为null),再依次记录每个元素的Key,Value。
    如果是自定义的class和struct,则先记录其序列化的长度(int,-1表示为null),再记录其序列化后的内容 

     二.消息头

          消息头固定长度为8。

         以登陆请求消息为例:LoginContract,C->S,MessageType为0 

    三.消息

           DeviceType取值: 0 - DotNet ,1 - Android ,2 - iOS       

    六.序列化原理

           在分布式通信系统中,网络传递的是二进制流,而内存中是我们基于对象模型构建的各种各样的对象,当我们需要将一个对象通过网络传递给另一个节点时,首先需要将其序列化为字节流,然后通过网络发送给目标节点,目标节点接收后,再反序列化为对象实例。

           为了将业务对象转换为二进制流,大家通常有两种方案可以选择:使用.NET自带的二进制序列化器,或,先将业务对象转换为字符串(比如xml),再将结果用类似UTF8进行编码得到字节流。 然而这两种方案都有缺陷。

    1..NET自带的二进制序列化器

          缺点:强侵入性,序列化结果臃肿,其size巨大,效率低下,加密困难。

    2.通过string进行中转

          缺点:需要自己打造协议对象与字符串之间的相互转换、序列化之后的结果的Size取决于我们协议对象与字符串之间相互转换时所采用的规则,同样也取决于我们的耐心程度 、同.NET自带的二进制序列化器,对象属性值的读取/设置、对象的创建等通常也是基于反射的,所以,效率同样存在问题。 

         基于上述,我选择采用自己的序列化方式,性能较.NET自带的二进制序列化器提高了7~8倍。  

    七.源码下载 

        (说明:压缩包中有SQL脚本,我使用的是Mysql数据库。客户端登陆账号为test01~test99,以及默认的testQQ。相应的好友关系可以查看服务端的TestFriendProvider类。 )

         安卓客户端下载

    ——————————————————————————————————————

    推荐阅读 :那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程 

  • 相关阅读:
    linux下解压命令大全
    关于伸展树的详细解析(E文)
    数据结构与算法汇总
    Linux下的压缩解压缩命令详解
    Linux Netcat 命令——网络工具中的瑞士军刀
    gethostbyname
    Html 转化为 PDF
    返回一个表
    sqlserver创建函数
    取不同类别的第一条数据
  • 原文地址:https://www.cnblogs.com/aoyeyuyan/p/6307123.html
Copyright © 2011-2022 走看看