1. 程式人生 > >如何巧妙地在基於 TCP Socket 的應用中實現使用者註冊功能?

如何巧妙地在基於 TCP Socket 的應用中實現使用者註冊功能?

      通常,在基於TCP的應用中(比如我開源的GGTalk即時通訊系統),當TCP連線建立之後,第一個請求就是登入請求,只有登入成功以後,伺服器才會允許客戶端進行其它性質的業務請求。但是,註冊使用者這個功能比較特殊,因為在註冊之前,還不存在這個UserID,就更不可能用這個UserID來登入了。

    所以,基於TCP的應用,使用者註冊功能一般是通過其它方式來實現的,比如,使用WebAPI,或者使用.NET Remoting等技術。 

      有沒有辦法可以不使用另外的技術而是直接基於當前的TCP連線來實現了?       

      經過我的摸索和實踐,找到了一個方法,可以達到這樣的效果,並且,即將推出的最新版本的GGTalk採用了這個方案。 這個方案,就是巧妙利用TCP應用的登入功能,在其基礎上加上特殊的標記來表達註冊行為。

      我們先看看GGTalk中的登入功能是如何做的。

一.現有的登入機制            

1.客戶端

      客戶端通過呼叫IRapidPassiveEngine的Initialize方法來與伺服器建立TCP連線,併發送登入請求,以及獲取登入結果。

        // 引數:
        //   userID:當前登入的使用者ID,由數字和字母組成,最大長度為10
        //   logonPassword:使用者登陸密碼。
        //   serverIP:伺服器的IP地址。
// serverPort:伺服器的埠。 // customizeHandler:自定義處理器,用於處理伺服器或其它使用者傳送過來的訊息 LogonResponse Initialize(string userID, string logonPassword, string serverIP, int serverPort, ICustomizeHandler customizeHandler);

       引數含義非常清晰,其返回值LogonResponse的定義如下:

      

        LogonResponse的LogonResult屬性說明了登入的結果,如果是Failed(登入失敗),FailureCause屬性就指明瞭失敗的原因。

2.服務端

        服務端通過回撥IBasicHandler的VerifyUser方法來驗證使用者的帳號資訊。

      bool VerifyUser(string systemToken, string userID, string password, out string failureCause);

        返回的bool值表示驗證是否通過,如果返回false(驗證不通過,登入失敗),則out引數failureCause指明失敗的原因,該引數會被返回到客戶端為LogonResponse的FailureCause屬性賦值。        

二.基本思路

        有了上面的基礎知識,我們就可以充分利用這個登入機制來實現註冊功能了。

        我們在服務端和客戶端共同做出這樣的約定:

(1)當登入的password引數以“#Reg:”開頭時,表示當前的行為就不是預設的登入行為,而是註冊行為。

         因為password一般是MD5加密的,所以不可能以“#Reg:”開頭,所以,這樣就避免了將正常的登入行為誤判為註冊行為。

(2)當password引數以“#Reg:”開頭,其接下來的內容即表示註冊所需的相關資訊,各資訊之間使用“;”分隔。

         比如,GGTalk使用的password的樣式如下所示:#Reg:[帳號];[密碼];[名稱];[簽名]。

         舉個例子,某個註冊的內容為:#Reg:10001;123456;david;加油!

(3)服務端發現當前的登入作為註冊行為時,VerifyUser方法一定返回false。並且,註冊的結果通過out的failureCause返回給客戶端。

(4)客戶端以註冊行為來使用IRapidPassiveEngine的Initialize方法時,其返回值LogonResponse的LogonResult屬性肯定是LogonResult.Failed。

(5)客戶端根據返回值LogonResponse的FailureCause屬性值來判斷註冊是成功還是失敗。FailureCause的可能值是:Succeed ,Existed ,Error。

三.具體實現

 1.服務端

      服務端按如下方式實現IBasicHandler的VerifyUser方法:

 

        public bool VerifyUser(string systemToken, string userID, string password, out string failureCause)
        {
            if(password != null && password.StartsWith(RegistActionToken)) //表示是註冊
            {
                //password內容示例: #Reg:10001;123456;david;加油!
                failureCause = this.Register(password);
                return false;
            }            

//此處驗證帳號密碼... return true; } private static string RegistActionToken = "#Reg:"; private string Register(string content) { try { //content內容示例: #Reg:10001;123456;david;加油! string[] parts = content.Substring(RegistActionToken.Length).Split(';'); string id = parts[0]; string pwd = parts[1]; string name = parts[2]; string signature = parts[3]; GGUser user = new GGUser(id, SecurityHelper.MD5String2(pwd), name, signature); RegisterResult res = this.Register(user); //將註冊的使用者資料儲存到DB return res.ToString(); //Succeed ,Existed ,Error } catch(Exception ee) { return "Error: 解析註冊字串報錯!" + ee.Message; } }

       程式碼比較簡單,就不再過多解釋了。

2.客戶端

      客戶端通過呼叫IRapidPassiveEngine的Initialize方法來完成註冊行為,程式碼如下所示:

        public void Register(string id, string pwd, string name, string signature)
        {
            string content = string.Format("{0}{1};{2};{3};{4}", RegistActionToken, id, pwd, name, signature);
            LogonResponse response = rapidPassiveEngine.Initialize("forRegister", content, serverIP, serverPort, null);
            string result = response.FailureCause; //Succeed ,Existed ,Error
            if (result == "Succeed") 
            {
                MessageBox.Show("註冊成功!");                
            }
            else if (result == "Existed")
            {
                MessageBox.Show("帳號已經存在!");
            }
            else
            {
                MessageBox.Show("註冊過程中出現錯誤!");
            }
        }

      注意,如果註冊成功,客戶端也是還尚未登入到伺服器的。

      註冊成功之後,再使用成功註冊的帳號和密碼呼叫正常邏輯的IRapidPassiveEngine的Initialize方法來登入到伺服器。

四.結語

      如此一來,我們就完全可以使用現有的TCP機制來實現使用者的註冊,而且在多端(PC、安卓、iOS)都可以這樣做。這樣就避免了僅僅為了一個註冊功能而需要去釋出一個Web站點或釋出一個Remoting服務(且不說Remoting還沒法支援安卓和iOS端),太不划算了。

      順便說一下,GGTalk接下來會不斷推出新的版本,不斷增強功能,而且UI方面也將進一步美化,敬請期待。

&n