通常,在基于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[];
string pwd = parts[];
string name = parts[]; string signature = parts[];
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方面也将进一步美化,敬请期待。

如何巧妙地在基于 TCP Socket 的应用中实现用户注册功能?的更多相关文章

  1. 用scala实现一个基于TCP Socket的快速文件传输程序

    这是用scala实现的一个简单的文件传输程序. 服务端 package jpush import java.io.{DataInputStream, File, FileOutputStream} i ...

  2. Android 基于TCP多线程通信实现群聊天的功能

    1.TCP多线程原理图 2.实现方法 (1)服务器端 (2)客户端 3.java后台代码 主界面 package com.lucky.test50socket2; import android.ann ...

  3. 移动网络应用开发中,使用 HTTP 协议比起使用 socket 实现基于 TCP 的自定义协议有哪些优势?

    HTTP 是应用层协议,TCP 是传输层协议(位于应用层之下),放在一起类比并不合适.不过猜测楼主是想对比 “标准 HTTP 协议” 还是 “自定义的协议(基于 TCP Socket)” . 一般来说 ...

  4. QT创建TCP Socket通信

    最近在学习QT,了解到QT可以进行SOCKET网络通信,进行学习,并建立一个简单的聊天DEMO.为了测试是否能与VS2012下的程序进行通信,在VS2012下建立一个客户端程序,进行通信测试,发现可以 ...

  5. 分布式消息总线,基于.NET Socket Tcp的发布-订阅框架之离线支持,附代码下载

    一.分布式消息总线以及基于Socket的实现 在前面的分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载一文之中给大家分享和介绍了一个极其简单也非常容易上的基于.N ...

  6. 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

    Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...

  7. Winfrom 基于TCP的Socket 编程

    基于TCP的Socket基础例子 服务端的代码 public partial class Form1 : Form { public Form1() { InitializeComponent(); ...

  8. 基于.NET Socket Tcp的发布-订阅框架

    基于.NET Socket Tcp的发布-订阅框架 一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已 ...

  9. IOS 基于TCP的socket通信详解(原创)

    最近在整理通信层相关知识,这篇文章是边整理边写的,有些地方可能不够准确,还请各位路过的大牛专家指出来.这次整理的socket通信是基于TCP的,实现方式是GCD形式,以下记录的都是些理论知识,方便自己 ...

随机推荐

  1. master.TableNamespaceManager: Namespace table not found. Creating...

    1.错误描述: 出现上述这个错误的原因是我之前已经安装了Cloudera Manager中的CDH,其中添加了所有的服务,当然也包含HBase.然后重新安装的时候,就会出现如下错误: Failed t ...

  2. [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher 题解报告

    来刷kuangbin字符串了,字符串处理在ACM中是很重要的,一般比赛都会都1——2道有关字符串处理的题目,而且不会很难的那种,大多数时候都是用到一些KMP的性质或者找规律. 点击标题可跳转至VJ比赛 ...

  3. 章节十六、9-Listeners监听器

    一.IInokedMethodListener 1.实现一个类来监听testcase的运行情况. package listenerspackage; import org.testng.IInvoke ...

  4. 为什么Kubernetes使用Pod作为最小调度单元

    一.Pod说明 Pod只是一个逻辑概念,一个原子调度单位,其优势在于 可以统一调度一组容器到指定的node上 共享资源,Pod的容器可以使用localhost进行通信,使用volume进行文件共享.使 ...

  5. C++程序设计学习

    第一章 预备知识 1.C++历史起源 由于C语言具有许多优点,比如语言简洁灵活:运算符和数据类型丰富:具有结构化控制语句:程序执行效率高:同时具有高级语言和汇编语言的优点等.与其他高级语言相比,C语言 ...

  6. FPGA、GPU、CPU三者各自的优缺点是什么呢?

    CPU: 英文全称:Central Processing Unit. 中文全称:中央处理器. 厂商:英特尔Intel. 功能:是一台计算机的运算核心和控制核心. 缺点:运算能力(最弱),核处理数(最少 ...

  7. java 数组定义

    1.方式一: 数组声明: int[] intArr ; String [] strArr; int [][] intArrs; 数组初始化: intArr = new int[6]; //一维数组 s ...

  8. 夯实Java基础系列7:一文读懂Java 代码块和执行顺序

    目录 Java中的构造方法 构造方法简介 构造方法实例 例 1 例 2 Java中的几种构造方法详解 普通构造方法 默认构造方法 重载构造方法 java子类构造方法调用父类构造方法 Java中的代码块 ...

  9. 【Django】中间件,csrf,缓存,信号

    中间件(middleware) 描述:Middlewares 是修改 Django request 或者 response 对象的钩子. 在django中,中间件其实就是一个类,在请求到来和结束后,d ...

  10. PyCharm中创建项目时,在所创建的python虚拟环境下的pip失效

    在这篇博文里,我简单地叙述了我在使用PyCharm创建一个flask项目时遇到的问题,以及我解决这个问题的过程.其中比较值得注意的点有:①PyCharm创建新项目时的解释器配置②Python虚拟环境的 ...