即时通讯是目前Internet上最为时尚的通信方式,但各种即时通讯软件都有各自不同的应用协议,并且由于嵌入式Linux应 用系统硬件条件较差,因此系统采用插件技术实现。系统可以选择使用多种聊天协议,还可以方便地进行扩展,添加新的协议或增加新的功能,同时满足了占用资源少,性能强等要求。即时通讯是目前Internet上最为时尚的通信方式,但各种即时通讯软件都有各自不同的应用协议,并且由于嵌入式Linux应用系统硬件条件较差,因此系统采用插件技术实现。系统可以选择使用多种聊天协议,还可以方便地进行扩展,添加新的协议或增加新的功能,同时满足了占用资源少,性能强等要求。
1 问题的提出
即时通讯俗称聊天软件,可以说是目前我国上网用户使用率最高的软件。无论是国内用户量的第一腾讯 QQ、ICQ以及微软的 MSN messenger 都是大众关注的焦点,他们能迅速地在网上找到朋友或工作伙伴,可以事实交谈和互传信息。而且,现在不少IM软件还集成了数据交换、语音聊天、网络会议、电子邮件的功能。
本文基于一个嵌入式的Linux多功能电话项目,该电话设备提供了许多讯息处理增值功能,如在线聊天,电话留言管理等。该设备是在320*240,4bpp LCD,和一个特殊的键盘。我们在此基础上开发一个简单便捷的用户聊天客户端软件。嵌入式系统的CPU采用了基于ARM体系架构的三星S3C2410,该芯片广泛应用于手持式以及便携式嵌入式系统中。
因为现在存在多种不同的流行聊天工具,所以我们的聊天系统希望能集成多种IM协议。由于我们的聊天程序可以使用多种聊天协议,如MSN、ICQ等。这样就面临一个问题,因为以后可能会添加更多新的协议或者对已有的协议进行更新,如:增强性能或添加新的功能,而且在我们的程序运行的时候用户指定了使用的协议,那么其他的协议在程序运行过程中就不需要使用了。这时候就可能需要重新编写调试整个程序,这位以后的工作带来了很多麻烦。同时我们的开发平台是在一个嵌入式平台上,由于硬件条件的限制,内容容量只有32M,CPU运算速度不快,而且我们的系统上还要运行一些其他的图形应用程序,所以在下一个应用程序必须满足占用资源尽可能少,性能要强的要求。
2 插件解决方案
2.1 解决方案简述
我们使用插件的方式来解决上面的问题,即框架加插件的软件开发方法。插件的本质在于不修改程序主体的情况下对软件功能进行扩展与加强,当插件的接口公开后,二次开发者都可以根据接口制作自己的插件来增加新的功能或改进某些方面的不足,也就是实现真正意义上的“即插即用”软件开发。框架加插件软件结构是将一个待开发的目标软件分为两部分,一部分为程序的主体或主框架,可定义为平台或框架,另一部分为功能扩展或补充模块,可定义为插件。使用平台加插件结构进行软件设计会给开发软件增加新的活力。
2.2 嵌入Linux 插件规范
在Linux系统中,插件以动态链接库形式实现。通过Linux 下的动态链接库技术,可以提高系统的扩展能力。程序编织一般需经编辑、编译、连接、加载和运行几个步骤。当使用动态链接库与用户程序的目标文件连接时,连接器只是作上标记,而不是把库代码复制到可执行文件中,仅当可执行文件运行时,加载器根据这个标记,检查该库是否已经被其它可执行文件加载进内存。如果已存在于内存中,则只要共享内存中已有的代码即可。动态链接库是程序函数,它们在设计和构建应用程序时为该程序所知。设计应用程序的主程序时使用程序框架或底板,这些程序动态链接库位于磁盘上同主程序分离的一些文件中。插件提供了灵活的升级、维护、以及许可策略。
2.3 插件与主体的功能分析
应用插件解决方案要分析哪些功能由主体完成,即平台的基本功能,哪些功能由插件完成,即需要扩展的插件功能。平台所完成的功能应为一个软件系统的核心和基础,这些基本功能既可为用户使用,也可为插件使用,又可以把平台基本功能分为两个不能,内核功能和插件处理功能。平台的插件处理功能用于扩展平台和管理插件,为管理插件操纵平台和与插件通信提供标准平台扩展接口。插件所完成的功能是对平台功能的扩展和补充,一般插件完成系列化功能,例如:Winanp的插件完成对播放效果的特殊效果处理。Winanp的Plug-ins其实就是能够为其提供增强型播放效果的插件。这种强型播放插件的种类非常丰富,比如输入输出解码、视觉效果的变化、声音音效的变化、通用类的显示歌词的插件等等。这些功能都有一些共性,可以进行集中管理,并且是可以定义出标准的插件接口。
3 关键技术
我们的程序时在Qt/Embedded基础上采用面向对象方法设计的,用c++语言开发的。因此我们的各种协议都是封装在类中。Qt/Embedded是一个基于C++的CUI库,具有很好的移植性。
3.1 插座设计
首先我们需要设计一个抽象类,它包括各种协议都使用的一些基本的操作,通过分析现有的IM协议我们抽象出一些操作,例如:登录服务器,断开和服务器的连接,添加删除联系人,发送消息,接受消息,发送接受文件等。在此基类中我们只给出纯虚拟函数。然后派生于此基类的各种协议类各自实现所做的操作。这样通过多态的方法,我们在主程序中只需要包含此基类就可无需知道子类中具体实现而可以使用各种子类对象。我们只需要规定基类中定义的一些统一的方法。
class ChatConnection : public QObject
{
public:
virtual bool openConnection(Account * account)=0;
virtual bool closcConnection()=0;
virtual void sendCommand(constQstring&prefix,constQstring&text)=0;
virtual void sendMessenge()=0;
...
};
在ChatConnection类中只定义了一些抽象函数接口,而把实现放在具体的协议实现中去。其中openConnection()是建立于服务器的连接,closcConnection()是断开连接,sendConnmand()负责发送各种命令,还有 sendMessenge()用来发送文本消息,receiveMessenge()接受联系人发来的文本消息等。
3.2 一个具体的插件设计
以下是Msn插件的定义样板:
extern"C"ChatConnection * GetProtoclObject(void);
class MsnConnection : public ChatConnection
{
public:
bool openConnection();
bool closeConnection();
void sendCommand(const QString& prefix,const QString&text);
...
};
其中extern"C"ChatConnection * GetProtoclObject(void);是共享库的对外接口函数的声明,通过调用GetProtoclObject(void)在共享库外获得共享对象。这样我们就可以得到实际使用的协议对象而不用在主程序中针对不同协议做不同的处理。
其中MsnConnection类的sendCommand()函数的实现如下:
int MsnConnection::sendCommand(const QString& prefix,const QString&text)
{
Qstring ackString,command;
int ack=getAck(); //Get an acknowledgement number
ackString.sprintf("%d",ack); //and convert it to a string
command=prefix+""+ackstring+""+text;
//Compose the command
writeData(command); //send the to the server
return ack;
}
其中prefix用来表示各种命令,ADD表示添加好友命令,CHG表示改变在线状态,FLN表示好友离线等。Ack是首先获得一个应答,并将其转换为String,之后组成一条命令写入socket。
3.3 动态装入过程
插件管理可以通过使用对动态链接装入器的dlopen、dlsym和dlclose函数调用来获取对共享目标文件的访问。dlerror以字符串的形式返回任何错误,这些错误信息字符串描述dl函数碰到的最后一个错误。在运行时,主应用程序使用绝对路径或相对于LD_LIBRARY_PATH的相对路径找的共享目标文件的句柄,并且从内存中取消该目标文件映射。
void * ProtocolLib=dlopen("./msn.so",RTLD_LAZY);
ChatConnection *(*pGetProtocol)(void);
pGetProtocol=(chatConnection *(*)(void)dlsym(ProtocolLib,"GetProtocolObject");
chatConnection * p=( * pGetProtocol)();
我们定义一个函数指针pGetProtocol来指向dlsym返回的函数入口地址,然后调用=( * pGetProtocol)()来获得协议对象。之后在主程序中就可以用P来进行协议的各种操作了。在编译共享库时使用附加选项-fPIC编译共享目标代码,以产生位置无关的代码,使用-shared选项将目标代码放进共享目标库中。在编译主程序时使用-Idl选项,这是链接选项,即主程序中的部分符号为动态链接库中的符号,也就是在运行时才需要用到共享文件。
4 结束语
采用插件的方式给我们的程序提供了很高的灵活性。可以在不修改程序主体的情况下对程序进行修改或增加新的功能。Linux中的共享目标代码库和动态链接装入器向应用程序提供了额外的功能。而且共享库的更新独立于应用程序,可以发行不带源代码的函数库,还降低了用户得二次开发的复杂度。
嵌入式IM是嵌套在网页上的,软件供应商,可以根据网站需求,设计出适合网站风格的IM产品,目前AnyChat SDK等国内音视频技术产品已在嵌入式上取得良好发展。目前已支持Windows、Unix、Linux(x86)、Linux(ARM)以及Windows Mobile平台,正在做Android平台的移植,远期将支持Symbian、iPhone等平台。利用AnyChat SDK的跨平台特性,可以实现嵌入式硬件设备(ARM平台)与Internet上的Windows平台实现语音、视频的交互。产品的跨平台特性,操作系统的多重选择,将使企业的应用领域更加的广阔。