社交媒體(朋友圈、微博、QQ空間)開發一網打盡,PC端移動端都有!——源碼來襲!


一.應用場景

      曾幾何時,社交媒體已經駐扎到了幾乎每個人的生活中。看看你身邊的朋友,有幾個不玩朋友圈的?就算他不玩朋友圈,那也得玩微博吧。再沒有底線,也得玩QQ空間。

      不過,作為程序員的我們,沒事還是少上這些社交媒體為妙。反而,我們應該去考慮——如何實現這些社交媒體的開發呢?

      我相信一定有不少朋友跟我一樣,思考過這個問題,今天我就把自己的成果分享給大家,不一定完美,但求拋磚引玉,請大家多指教!

二.運行效果

      下面是我做好的PC端和安卓端的運行界面。讓大家先有一個直觀的了解。后面我會進一步分享我設計的過程。

       1.查看我的“圈子”                         2.查看“testQQ”的主頁                  3.發送動態

                  

      

三.開發要點

1.C\S架構——服務端與客戶端的核心類——兩大引擎

      本文所實現的朋友圈功能是基於NPush構建的,其核心組件是用於社交媒體開發的服務端引擎IServerEngine和客戶端引擎IMomentsClient 。

(1)服務端引擎IServerEngine:

    //NPush服務端引擎接口。
    public interface IServerEngine
    {  
//NPush服務器配置選項。必須在Initialize方法調用之前設置才有效。 NPushConfiguration Configuration { get; set; } //日志文件的路徑。默認為運行目錄下的AppLog.txt文件。 string LogFilePath { get; set; }
//當前連接數。 int UserCount { get; }
//獲取在線用戶列表。 List<string> GetUsers();
//初始化。 // 參數: // port: // 為客戶端提供服務的端口 // verifier: // 用戶帳號密碼驗證器。如果不需要驗證,可以傳入null。 // friendProvider: // 好友關系提供者 void Initialize(int port, IUserVerifier verifier, IFriendProvider friendProvider, IMomentsPersister persister); }

(2)客戶端引擎IMomentsClient :

public interface IMomentsClient : IDisposable
    {    
//與節點服務器之間的TCP連接是否正常? bool Connected { get; }
// 當前對象是否已經被釋放? bool IsDisposed { get; }
//當與節點服務器之間的TCP連接斷開時,觸發此事件。(連接斷開將會自動重連) event CbGeneric ConnectionInterrupted;
//當朋友圈有新的動態時,觸發該事件。參數:ownerID - infoType - pullIndex event CbGeneric<string, int, long> NewMomentNotified;
//當朋友圈有我相關的新評論時,觸發該事件。參數:remarkUserID - ownerID - pushIndex - remarkType - //content - tosb event CbGeneric<string, string, long, int, string, string> NewRemarkNotified;
//當同名的帳號登錄當前連接的服務器時,自己將被擠掉線(不再自動重連),觸發此事件。 event CbGeneric PushedOut;
//當與服務器重連上並登錄完成后,觸發此事件。請注意查看事件參數LoginResultType,如果登錄失敗,則表示當前客戶端實例已經失效。 event CbGeneric<LoginResultType> RelogonCompleted;
//當朋友圈消息被刪除時,觸發此事件。參數:ownerID - pushIndex event CbGeneric<string, long> RemoveMomentNotified; //當評論被刪除時,觸發此事件。參數:ownerID - pushIndex - remarkPassiveID event CbGeneric<string, long, string> RemoveRemarkNotified; //當調用Pull方法請求moment列表,得到服務器的回復時,觸發此事件。 event CbGeneric<UserMoments> ResponseMomentReceived;
//初始化,並登錄到服務器。 // 參數: // serverIP: // 服務器的IP // port: // 服務器的端口 // userID: // 登錄帳號 // pwd: // 登錄密碼 // 返回結果: // 登錄結果 LoginResultType Initialize(string serverIP, int port, string userID, string pwd);
//拉取我的朋友們發的消息。當收到服務器回復,將觸發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); // 在朋友圈發送消息。 // 參數: // msgType: // 消息類型。 // content: // 消息內容 // tag: // 附加tag // 返回結果: // 返回PassiveID string Push(int msgType, byte[] content, string tag);
//朋友圈某條消息評論、點贊、回復評論。 // 參數: // ownerID: // 目標消息的發送者ID // momentPushIndex: // 目標消息的PushIndex // remarkType: // 評論的類型。自定義。比如0表示贊,1表示評論,2表示回復。 // content: // 評論或回復的內容 // tosb: // 如果是評論回復,則@用戶的帳號 // 返回結果: // 返回PassiveID string Remark(string ownerID, long momentPushIndex, int remarkType, string content, string tosb);
//刪除自己發的某條消息。 // 參數: // momentPushIndex: // 目標消息的PushIndex void RemoveMoment(long momentPushIndex);
//刪除自己發的某條消息。 // 參數: // momentPassiveID: // 目標消息的PassiveID void RemoveMoment(string momentPassiveID);// 刪除自己發的某條評論。 // 參數: // ownerID: // 目標消息的發送者ID // momentPushIndex: // 目標消息的PushIndex // remarkPassiveID: // 目標評論的PassiveID void RemoveRemark(string ownerID, long momentPushIndex, string remarkPassiveID); }

       以上是兩個類的定義,更具體的相關說明我將在下文中為大家逐一介紹。

2.基本動作——推(push)與拉(pull)

      無論朋友圈、微博還是QQ空間,都有兩個基本動作——推(push)與拉(pull)。推出消息,也就是發動態;拉取消息也就是刷動態。當然,更進一步來說,所謂的社交媒體系統,本質上還是一個通信系統。“發送”與“接收”是任何通信中亘古不變的主題。

      所以,首先得設計好這兩個基本動作。

(1)推(push)——API說明

        // 在朋友圈發送消息。     
        // 參數:
        //   msgType:
        //     消息類型。      
        //   content:
        //     消息內容       
        //   tag:
        //     附加tag     
        // 返回結果:
        //     返回PassiveID
        string Push(int msgType, byte[] content, string tag);

      push方法比較簡單,msgType這個參數用在服務端對消息做一些自定義處理的時候。目前的例子中服務端對消息是自動轉發的,不涉及到自定義處理。

(2)拉(pull)——API說明

        // 拉取我的朋友們發的消息。當收到服務器回復,將觸發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);

     pull方法的兩個重載,前者是用於拉取“圈子”的動態,后者是用於拉取某位“個人”的動態,這兩種模式我將在接下來的“3.“圈子”VS“個人”——兩種模式”中進行介紹。

      至於startPushIndex,則涉及到我對於“動態”的設計——每條動態都有一個全局唯一的Index。而Pull方法每次都是默認拉取20條冬天,因此,在拉取動態的時候,我們就可以指定起始的Index,也就是startPushIndex,從而獲取指定的20條動態。關於這個Index,在接下來的3中,我們可以進一步了解。

      而latest參數則是表示上拉還是下拉——上拉就是向上刷新,刷取新動態;下拉也就是刷取舊動態。經常玩朋友圈的朋友對這兩個操作一定不會陌生。

(3)消息接收的地方——ResponseMomentReceived事件

// 當調用Pull方法請求moment列表,得到服務器的回復時,觸發此事件。
 event CbGeneric<UserMoments> ResponseMomentReceived;

正如注釋所指出的,每次拉取的動態都將由這個事件來返回。我們進一步來看看事件參數UserMoments的定義:

  public class UserMoments
    {
        public UserMoments();
//是否是請求目標用戶所發的朋友圈列表。 public bool IsOwnerList { get; set; } public Dictionary<long, PushedMessage> MessageList { get; set; }
//如果是請求自己的朋友圈動態,則為自己的ID。如果是請求某個好友Push的消息列表,則為好友的ID。 public string UserID { get; set; } }

      其中MessageList 就是動態信息的集合,這個字典的key就是我們之前說到的Index,字典的Value:PushedMessage,這個我們將在“4.數據庫實體”中進一步說明。

3.“圈子”VS“個人”——兩種模式

      我們知道,朋友圈中既可以查看圈子中的動態,也可以查看某個人的動態。所謂圈子,在朋友圈和QQ空間中就是好友關系,在微博中就是關注關系——實質上並沒有區別,所以我們就用好友來概括。

      這個好友關系定義在服務端。來看我的簡單實現:

   public class TestFriendProvider : IFriendProvider
    {
        private List<string> list = new List<string>();

        public TestFriendProvider()
        {
            for (int i = 0; i < 99; i++)
            {
                list.Add("test" + i.ToString("00"));
            }
            list.Add("testQQ");
        }

        public List<string> GetFriends(string userID)
        {
            if (string.Equals(userID, "testQQ"))
            {
                List<string> l = new List<string>();
                l.Add("test01");
                l.Add("test02");
                l.Add("test03");
                return l;
            }
            return this.list;
        }
    }

      這個類的實例將注入到服務端引擎中

this.serverEngine.Initialize(int.Parse(ConfigurationManager.AppSettings["Port"]), new MyUserVerifier(), new TestFriendProvider(), this.persister);

      服務端引擎屆時將會回調GetFriends方法,從而將某用戶發送的動態推送給他的好友。我在例子中采用的是簡單的實現,大家要實現自己的朋友關系,只需要自定義IFriendProvider接口的實現即可。

      順便介紹下服務端的另一個注入的接口,IUserVerifier,用於注入用戶登錄驗證的邏輯。下面看下例子中的簡單實現:

   class MyUserVerifier:IUserVerifier
    {
        private List<string> list = new List<string>();

        public MyUserVerifier()
        {
            for (int i = 0; i < 10; i++)
            {
                list.Add("test" + i.ToString("00"));
            }
            list.Add("testQQ");
        }
        public bool VerifyUser(string userID, string password)
        {           
            return this.list.Contains(userID);
        }
    }

      至於某個人的動態則根據對方的ID進行獲取即可。

4.數據庫實體

(1)PushedMessage 

    [Serializable]
    public class PushedMessage : IEntity<long>
    {
        public const string _IsValid = "IsValid";
        public const string _OwnerID = "OwnerID";
        public const string _PassiveID = "PassiveID";
        public const string _PushIndex = "PushIndex";
        public const string TableName = "PushedMessage";

        public PushedMessage();
        public PushedMessage(string owner, long index, PushMomentsContract contract);
        //     持久化數據庫中的自增ID。
        public long AutoID { get; set; }
        public byte[] Content { get; set; }
        public int InfoType { get; set; }
// 是否有效。如果消息被發送者刪除,則變成無效。 public bool IsValid { get; set; } public string OwnerID { get; set; } // 由客戶端生成的ID。(UserID_時間序列號) public string PassiveID { get; set; } public long PushIndex { get; set; } public DateTime PushTime { get; set;
// 評論列表。 [NotDBField] public List<Remark> RemarkList { get; set; } public string Tag { get; set; } public long GetPKeyValue(); public override string ToString(); }

    對應的數據庫表結構:

(2)PulledMessage 

   [Serializable]
    public class PulledMessage : IEntity<long>
    {
        public const string _GuestID = "GuestID";
        public const string _PullIndex = "PullIndex";
        public const string TableName = "PulledMessage";

        public PulledMessage();
        public PulledMessage(string guest, PushedMessage msg, long index);

        public long AutoID { get; set; }
        public string GuestID { get; set; }
        public string OwnerID { get; set; }
        public long PullIndex { get; set; }
        [NotDBField]
        public PushedMessage PushedMessage { get; set; }
        public long PushIndex { get; set; }

        public long GetPKeyValue();
        public override string ToString();
    }

對應的數據庫表結構:

(3)momentremark

   [Serializable]
    public class MomentRemark : IEntity<long>
    {
        public const string _AutoID = "AutoID";
        public const string _IsValid = "IsValid";
        public const string _OwnerID = "OwnerID";
        public const string _PassiveID = "PassiveID";
        public const string _PushIndex = "PushIndex";
        public const string TableName = "MomentRemark";

        public MomentRemark();
        public MomentRemark(string remarkUser, RemarkContract contract);

        public long AutoID { get; set; }     

// 評論的內容。 public string Content { get; set; }
// 是否有效。如果消息被發送者刪除,則變成無效。 public bool IsValid { get; set; }

// 目標Moment的Owner的ID。 public string OwnerID { get; set; }
// 由客戶端生成的ID。(UserID_時間序列號) public string PassiveID { get; set; }
// 目標Moment的PushIndex。 public long PushIndex { get; set; }

// 評論的時間。 public DateTime RemarkTime { get; set; }
// 評論的類型。 public int RemarkType { get; set; }

// 評論者的ID。 public string RemarkUserID { get; set; }

// 消息標記,10個字符(月日時分秒)。由客戶端賦值。 public string Token { get; set; }

// 如果是評論回復,則@用戶的帳號 public string Tosb { get; set; } public long GetPKeyValue(); public Remark ToRemark(); public override string ToString(); }

對應的數據庫表結構:

      至於如何與數據庫交互,這個我已經封裝好了 

 this.persister 
= new DBMomentsPersister(new MysqlDataConfiguration(ConfigurationManager.AppSettings["DBIP"], int.Parse(ConfigurationManager.AppSettings["DBPort"]), ConfigurationManager.AppSettings["UID"], ConfigurationManager.AppSettings["Pwd"], ConfigurationManager.AppSettings["DBName"])); this.serverEngine.Initialize(int.Parse(ConfigurationManager.AppSettings["Port"]), new MyUserVerifier(), new TestFriendProvider(), this.persister);

      只用構造好注入到服務端引擎即可。 

四.部署說明

    源碼下載 

    安卓客戶端下載

    說明: 

    1.壓縮包中有SQL腳本,我使用的是Mysql數據庫。

    2.服務端客戶端均有配置文件

    3.客戶端登陸賬號為test01~test10,以及默認的testQQ。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM