我的IM - 基础篇[2] - 基于UDP通讯的IM设计[开篇]

呵呵, 在上一篇文章中的末尾,我粘贴了来自我的框架部分截图,相信大家也很感兴趣,而且我也会毫不保密的继续为大家介绍精彩的IM技术讲解。

这篇文章由我来讲解下基于IM的UDP通讯协议设计(开篇 - 1)

  大家都用过QQ吧?经常用吗?知道QQ的原理吗?会“拆”QQ吗?呵呵,作为一个IM研究人员,就要经常的对现有市场产品进行剖析。

QQ,是基于UDP通讯协议的,这个毋庸置疑,我来剖析下QQ(以QQ2009为例):

  当大家打开QQ后,他会自动开启UDP端口若干(4000~6000)2个分段依次累加,当时我就在想,为什么他开那么多端口呢?有必要开那么多端口吗?

呵呵 那么好,前言,现在开始本次讲解。

  现在的即时通讯软件中,大部分采用了UDP协议,原因有以下几个:

  1. UDP协议不用一直保存用户SOCKET,也就是断开式的,这样节省资源

  2. 为了服务器转发各种消息得到了便利

  3. 为了更好的减少异常的出现(比如用TCP/IP时的异常断开?)

我在项目中也是采用的UDP通讯协议,大体的框架图可以看上一篇的,那么开端口,为什么像QQ那样开那么多端口呢?

呵呵 这里我也给大家解答下,作为IM编写者,要时刻保持一种概念:一个QQ是可以支持不同协议簇的,而每个协议簇又包括了该协议簇的各种命令

这就是为什么QQ会开有多个UDP端口,因为为了支持不同的协议!!

  我想看这篇文章的人,很多人在写C/S模式软件的时候是不是仅仅拥有1个协议呢?

  呵呵 还不理解?我打个比方,你的IM 注册,登陆用户,获取用户信息,聊天。。处理分组操作,处理好友操作是不是都是开1个UDP端口呢?

如果不是,那是因为什么开那么多呢?

  然后大家用这一个UDP端口接收后,会有“命令树”(我们的俗称),说白了无非就是下面的形式:

  


//通过第一个参数,判断第二个参数,来调用不同的处理方法
            switch (Sp_Orders[0])
            {
                
//修改信息命令
                case "Update":
                    
//判断要修改的项目是什么
                    switch (Sp_Orders[1])
                    {
                        
//修改个人签名
                        case "Pro":
                            
this.UpdateToData("Pro", Sp_Orders[2], Sp_Orders[3]);
                            
break;
                        
//修改昵称
                        case "NickName":
                            
break;
                        
//修改年龄
                        case "Age":
                            
break;
                        
//修改个人详细信息
                        case "Info":
                            
break;
                        
//更新上线端口
                        case "Port":
                            
//将QQ,上线端口传入
                            this.Port = Sp_Orders[3];
                            
this.Number_MySelf = Sp_Orders[2];
                            
this.Update2Port(Sp_Orders[2], Sp_Orders[3]);
                            
break;
                            
//其他
                        default:
                            
return;  
                    }
                    
break;
                
//聊天命令
                case "Chk_Online":
                    
this.Chk_User_Online(Sp_Orders[1]);  //调用检测用户上线方法
                    break;
                
//其他情况
                default:
                    
break;
 
 
呵呵,请大家不要学习上面的方式,我这里举例说明知识为了让不明白的人理解下。
这样的命令树是不好的,因为什么呢?
1. 如果我的命令很多(一般IM协议都很多),那岂不是耦合度很强?而且会很多代码写在一个方法体内。
2. 看起来就琐碎。。。你不觉得吗?
这里只是简单说说,那么我们来说说QQ是根据什么开端口的(比如):
  
我们定义了一个枚举:

 

 


public enum NetworkProtocol
{
    BASE,                //支持一般的协议簇(与服务器一般交互)
    FILE_TRANSFER,  //文件传送协议簇
    VIDEO,              //视频协议簇
    VOICE               //语音协议簇
}

 

 

那么,这样就很清晰了,为了已更好的方式优化IM程序,我们会为每个协议簇单门指定端口进行监听。

这样呢,就是4个UDP了对不?

 

好了,那么现在就需要继续上次讲解的知识了,我们使用具有包含关系的PortListener 和MessageListener成功的监听了4个UDP端口,

什么? 您不会还要用刚才的命令树来判断命令吧?????

 

呵呵 让我们开拓思路下,什么方法能耦合度低,而且易维护,简单,方便呢?

我这里提出来我的项目经验,呵呵 直接给出大家对于这样很伤脑筋的正确解决途径。

 

我先来大概的讲下我们的通讯,作为UDP通讯,我们会有2个很大的枚举,一个叫做"IN_NETWORK_COMMAND"另一个叫做

"OUT_NETWORK_COMMAND",这样的分工,大家待会儿就会明白好处了。

 

 

 

那么好,"IN_NETWORK_COMMAND" 主要用来保存我当前接受到的包协议命令

"OUT_NETWORK_COMMAND"主要用来保存我们要发送的包协议命令

来看下我项目中这2个枚举(包含于我的UML通讯框架中):

 

不知道,大家可否看出其中的秘密? 呵呵 细心的人一定看到了,我的,"IN_NETWORK_COMMAND" 和"OUT_NETWORK_COMMAND"

都是一一对应的,这就是解决方案了!

 

么好,接下来继续,原来我们定义的协议最早是什么样子呢?

我给出这里大部分人的协议(比如登录):Login$用户名{**}密码{**}{|*End*|}

这个就是我所见过的很多人的关于用户登陆的自定义协议,对方还很有根据:跟我说“我接收到这样的命令后,首先呢,会看看是否存在{|*End*|}

,如果存在呢,那当然这条命令就是完整的了,然后呢用{**}分割,就能得到用户名和密码啦!”

 

呵呵,那么好,关于这位朋友的自定义协议命令,我不想多说什么,因为我会在下次讲解中来讲解项目中正确的解决方案,这次实例就拿他的这个协议作比喻:

我们如何来区分当前协议所属的协议簇呢? 呵呵 还记得刚才我们定义的协议簇枚举吗?enum类型可以被转换为int!

这就够了,那么在原有的基础上我们可以加上:

Login$用户名{**}密码{**}0 (<--这个0代表协议簇编号,是由枚举强转的){**}{|*End*|}

 好了,我们在自定义的命令中已经插入了这条命令所属的协议编号,我们继续改,不用Login$头作为命令自定义头了,我们还用刚才的方法将你想要的命令编号

从命令枚举中,强转为int

这样的话,这个自定义命令变为:

用户名{**}密码{**}0 (<--这个0代表协议簇编号,是由枚举强转的){**}0 (<--这个0代表命令编号,是由枚举强转的){**}{|*End*|}

 

呵呵,这次看起来是不是舒服多了? 大多数可能认为,这样有什么好处呢?到了服务端不是照样要拆分吗?

呵呵 这样的好处多多,会由下次课来为大家讲解为什么这样的采用命令编号效果会好,那么我提前告诉大家下。

下次讲解的题目叫做"我的IM - 基础篇[3] - 基于UDP通讯的IM设计[基于XML格式的网络通讯协议]"

呵呵这里埋下伏笔,有兴趣的朋友可以想象有什么具体好处呢?

 

好了,我们回顾下本次讲解的内容:

1. 提出了不正确命令树写法

2. 抛出了大部分人自定义的通讯协议命令,并且更正了部分参数

3. 解答了为什么会开多个UDP端口

4. 提出了协议簇枚举概念

5. 提出了自定义协议命令(IN/OUT)枚举初步方案

 

呵呵 对于本次讲解,您还满意吗?  下面的讲解将会更加精彩!!!

你可能感兴趣的:(Socket)