zoukankan      html  css  js  c++  java
  • 如何巧妙地在基于 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方面也将进一步美化,敬请期待。

  • 相关阅读:
    javascript运行机制
    ios-scroll 和系统设置overflowscroll后卡顿
    input属性autocomplate背景颜色
    img 的onload事件和complate事件区别
    image图片之间缝隙bug解决方法
    gulp使用指南
    getQueryString
    decodeURI()和decodeURIComponent()函数
    css样式实现水平方向滚动
    I2C
  • 原文地址:https://www.cnblogs.com/justnow/p/11725110.html
Copyright © 2011-2022 走看看