循序漸進做項目系列(3):迷你QQ篇(1)——實現客戶端互相聊天


《循序漸進做項目系列迷你QQ篇》將陸續介紹客戶端聊天,文件傳輸,加好友,群聊,包括語音聊天,視頻聊天,遠程桌面等等需求如何實現,感興趣的朋友可以持續關注。考慮到某些需求較為復雜,本系列采用成熟的通信框架ESFramework來做,而不是從socket做起,當然這與本人才疏學淺也有莫大的關系,如果大家不嫌棄小弟寫得太“low”,還請捧個人場,順便給予鼓勵!

     言歸正傳,今天就是要實現一個最簡單的功能:客戶端互相聊天。

    

一·部署通信設備

     參見 循序漸進做項目系列(1):最簡單的C/S程序——讓服務器來做加法

        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            IRapidPassiveEngine clientEngine = RapidEngineFactory.CreatePassiveEngine();//創建客戶端通信引擎
            ClientHandler clientHandler = new ClientHandler();//創建客戶端消息處理器       
            Form_Login loginForm = new Form_Login(clientEngine, clientHandler);       
            loginForm.ShowDialog();//登陸窗會話完成才能運行主窗體
            Form_Client clientForm = new Form_Client(clientEngine, loginForm.SelfUserID);
            clientHandler.ClientForm = clientForm;
            Application.Run(clientForm);
        }

二·登陸窗設計

    public partial class Form_Login : Form
    {
        private IRapidPassiveEngine clientEngine; 
       
        private  ClientHandler clientHandler;

        public string SelfUserID//本人用戶ID,該屬性主要是為了暴露給主窗體用來標識用戶
        {           
get { return this.textBox_UserID.Text; } } public Form_Login(IRapidPassiveEngine _clientEngine, ClientHandler _clientHandler) { InitializeComponent(); //傳入通信引擎和消息處理器主要是為了登陸的時候進行通信引擎初始化操作 this.clientEngine = _clientEngine; this.clientHandler = _clientHandler; } private void button_login_Click(object sender, EventArgs e) { //通信引擎初始化,與目標服務器建立TCP連接 LogonResponse logonResponse = this.clientEngine.Initialize(this.textBox_UserID.Text, this.textBox_Password.Text, "127.0.0.1", 2000, clientHandler); if (logonResponse.LogonResult == LogonResult.Succeed) { this.DialogResult = DialogResult.OK; } else { MessageBox.Show("登陸失敗!" + logonResponse.LogonResult.ToString() + logonResponse.FailureCause); } } }

 三·客戶端主窗體設計

    public partial class Form_Client : Form
    {
        private IRapidPassiveEngine clientEngine;

        private string selfUserID;

        public Form_Client(IRapidPassiveEngine _clientEngine,string _selfUserID)
        {
            this.clientEngine = _clientEngine;
            this.selfUserID = _selfUserID;
            this.Text = _selfUserID;//在窗體上顯示本用戶ID
            InitializeComponent();
        }

        private void button_Send_Click(object sender, EventArgs e)
        {
            string msg = this.textBox_Input.Text;//聊天消息
            Byte[] msgCode = Encoding.UTF8.GetBytes(msg);//轉碼以備發送
            string targetUserID = this.textBox_TargetUserID.Text;//從輸入框讀取待發送用戶ID //利用通信引擎發送消息
            this.clientEngine.CustomizeOutter.Send(targetUserID, InformationType.Chat, msgCode);
            this.ShowChatMsg(this.selfUserID, msg);         
        }
        //將聊天消息按一定格式顯示在界面上
        public void ShowChatMsg(string UserID, string msg)
        {
            this.richTextBox_Output.AppendText(UserID + "  " + DateTime.Now.ToString() + "\n");
            this.richTextBox_Output.AppendText(msg + "\n");
            this.richTextBox_Output.ScrollToCaret();
            this.textBox_Input.Text = "";
        }
    }

四·客戶端收到消息后進行處理

 public class ClientHandler: ICustomizeHandler
    {
        private Form_Client clientForm;

        //該屬性是為了在外部將clientForm注入,之所以不在構造函數中傳入是因為當時clientForm尚未創建
        public Form_Client ClientForm
        {
            get { return clientForm; }
            set { clientForm = value; }
        }
       
        public void HandleInformation(string sourceUserID, int informationType, byte[] info)
        {
            if (informationType == InformationType.Chat)
           {
               if (this.clientForm.InvokeRequired) //不是主線程時
               {
                   //轉換為主線程
                   this.clientForm.Invoke(new ESBasic.CbGeneric<string, int, byte[]>(this.HandleInformation), sourceUserID, informationType, info);
               }
               else
               {
                  string msg = Encoding.UTF8.GetString(info);//解析消息
                  this.clientForm.ShowChatMsg(sourceUserID, msg);//顯示消息
               }
           }
        }

        public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info)
        {
            throw new NotImplementedException();
        }

五·總結

    源碼下載:MiniQQ1.0.zip

      由於代碼中盡量做到規范命名,並做了詳細的注釋,所以沒有再花更多的文字段落去說明,相信大家也能很容易看懂。整個邏輯十分簡單,對於新手有兩點需要注意一下:

1.各個類之間通過參數傳遞從而完成合作,傳參方式有的是通過構造函數,有的是通過屬性注入,新手朋友們可以自己比較一下兩種方式的適用場合。

2.其中有一處重構,就是聊天的時候既要顯示自己發的消息又要顯示收到的消息,這時候代碼基本上是一樣的,所以我將其重構了。至於什么時候需要重構,如何重構,新手朋友們可以自己多多揣摩。

      這兩點都是平時做項目中經常遇到的問題,是需要熟練掌握的。

      當然,最重要的一點還是循序漸進做項目咯!做的多了這些東西自然就會熟練了,寫程序時就能夠下筆如有神了!

 


免責聲明!

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



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