目录
摘要
使用Hashtable建立消息映射表的问题
消息映射类的设计和实现
消息映射类在SockBase中的使用
摘要
在上一篇文章<<可扩展的SockBase设计和实现(1)>>中,我们消息映射表是通过简单的Hashtable表来建立的.这样做,功能相对太简单,而且不便于扩展.而且Hashtable中的一些特性是我们不必要使用的.所以在这里,我们直接使用自定义的消息映射类(集合类CommandHandlerList和消息命令/处理函数类CommandHandler.)来建立消息映射表.
使用Hashtable建立消息映射表的问题
我们的消息映射表,要求是要一个消息命令,能对应着一个/或多个处理函数.同时对于存储整个消息映射表,要求能够很方便的增加/删除其中的条目.而且能够以比较方便的形式从表中找到消息对应的处理函数用来进行调用.
Hashtable是.NET Framework自带的一种键/值对集合. 当某个项目加入集合时,HashTable即调用键值的GetHashCode方法,由于所有的类都是从System.Objec继承的,所以调用该方法即可确定该类的哈希代码并且按该代码排序存储。从性能的角度看,因为键值搜索仅限于具有同样哈希代码的键值,所以HashTable能够很快地从集合中检索任意一个元素,从而减少了必须通过检查以发现匹配的键值的数量。然而,因为插入到集合中的每个对象-键值对都必须产生相应的哈希代码,所以项目插入的代价就有点高了。因此,HashTable主要运用在按照任意键值反复检索大量相对静态的数据这一场合下。
由于我们的消息全部都是字符串,所以对于对象的比较而言只要进行字符串的匹配,不需要通过Hashtable得到HashCode进行比较.同时,由于Hashtable只为键/值对,直接使用不能带来更大的扩展性.所以我们选择放弃直接使用Hashtable.使用我们自定义的类.
消息映射类的设计和实现
由上面的说明可以得到,我们要自定义消息映射类.首先定义每一个消息映射表中每一个消息及其处理函数的类.
由于收到消息和实际的处理函数不在同一个类中,而且应该是与具体的类无关的,所以我们使用了.NET提供的委托.由于通过Sockets发送/接收的消息都是字符串,所以消息处理函数的参数也就是收到的字符串,所以此委托所对应的函数的参数为string类型.而且为了方便一些特殊情况,比如收到的一个消息可以会触发多个处理函数.我们将其定义为事件.定义如下:
public delegate void CommandEventHandler(object sender , CommandHandlerArgs e);
public class CommandHandlerArgs:System.EventArgs
{
private string m_cmdText;
public string CommandText
{
get{return m_cmdText;}
set{m_cmdText=value;}
}
public CommandHandlerArgs(string CmdText)
{
m_cmdText=CmdText;
}
}
在这个CommandHandlerArgs里面,m_cmdText就是消息的参数.
现在可以开始定义我们的CommandHandler类了
public class CommandHandler
{
private string m_CmdPrefix;
private event CommandEventHandler m_exeCmd;
public string CommandPrefix
{
get{return m_CmdPrefix;}
set{m_CmdPrefix=value;}
}
public event CommandEventHandler CommandEvent
{
add{m_exeCmd+=value;}
remove{m_exeCmd-=value;}
}
public CommandHandler(string cmdPrefix)
{
m_CmdPrefix=cmdPrefix;
}
public void ExeCommand(string cmdText)
{
CommandHandlerArgs e=new CommandHandlerArgs(cmdText);
if(m_exeCmd!=null)
m_exeCmd(this,e);
}
}
在CommandHandler中,我们定义了一个Event,用来设置消息处理函数.同时,为了对接收到的消息和消息映射表中的消息进行匹配,加入了一个m_CmdPrefix,用来设置消息命令.还有就是一个直接运行的函数ExeCommand,通过此函数的参数cmdText,将Socket收到的消息传给消息处理函数.
现在就只剩消息映射表的实现了.既然是表,其中存储的为集合.为了方便,我们直接从CollectionBase 继承.代码如下:
public class CommandHandlerList:CollectionBase
{
public CommandHandler Add(CommandHandler cmdhandler)
{
List.Add(cmdhandler);
return cmdhandler;
}
public void RemoveAt(int index)
{
List.RemoveAt(index);
}
public void Remove(CommandHandler cmdhandler)
{
List.Remove(cmdhandler);
}
public int Length
{
get{return this.Count;}
}
public void Clear()
{
List.Clear();
}
}
其实这个CommandHandlerList类很简单的,就是直接用来存储各CommandHandler的.
好了,到现在,基本的消息映射表的结构完成了.现在就开始建立,运行我们这个消息映射表.
消息映射表在SockBase的使用
首先,把原有的使用Hashtable的list改成使用我们自定义的CommandHandlerList.
//待处理的命令处理集合
protected CommandHandlerList m_CommandHandlerList;
修改消息派发函数CmdHandler 中对消息进行查询调用的代码,修改后的代码如下:
foreach(CommandHandler cmd in m_CommandHandlerList)
{
if(cmd.CommandPrefix==cmdList[0])
{
cmd.ExeCommand(cmdText);
break;
}
}
最后就是初始化消息映射表的部分了.类Client_ListenThread中的LoadCommandHandlerList函数,修改后的代码如下:
public void LoadCommandHandlerList()
{
m_CommandHandlerList.Add(new CommandHandler( “GetFile” ).CommandEvent+=new CommandEventHandler(GetFileHandler);
m_CommandHandlerList.Add(new CommandHandler(“FileOK”)).CommandEvent += new CommandEventHandler(FileOKHandler);
}
到这个时候,我们的将原有的Hashtable换成我们自定义的类的消息映射表就全部完成了.
总结
由于Hashtalbe并不是很适用于我们这个自定义的消息映射表,所以我们换成了我们自己实现的CommandHandler和CommandHandlerList.