中国Linux公社论坛's Archiver

yunfan 发表于 2005-1-6 15:48

Eva开发文档1 ---------- libeva 类描述

借鉴了 openq 和 lumaqq 的很多东西。

在包解析的思路上,基本遵循了 lumaqq 的方案, 类命名上,也基本按照 lumaqq 的类命名方式。 目前 libeva 还没有完全完工, 但是基本的框架是有了,为了方便大家扩展这个库,加入更多的功能,我现在写出一个 libeva 类的基本描述。

对于开发者而言,希望尽量按照目前的风格编写代码。 当然有什么建议,只管提好了。

[b][size=18]libeva  类描述[/size][/b]

[color=red]libeva  类说明[/color]

libeva 是腾讯即使通讯协议的一个封装库, 包括对发送的数据的格式处理,加密
以及,对接受到的数据的解密和解析。

[color=blue]注:<- OutPacket 表示继承自, 其他类推[/color]

[b]libeva 库目前包含以下文件:[/b]

[color=red]evadefines.h 一些用到的全局变量[/color]



[color=red]evapacket.cpp evapacket.h 所有发送和接收类的父类
包含以下类:[/color]
[b]Packet[/b]   所有包解析的父类
[b]OutPacket[/b] 所有发送包的父类              <- Packet
[b]InPacket[/b]  所有接收包的父类              <- Packet



[color=red]evaaddfriend.cpp  evaaddfriend.h  添加,删除好友的处理
包含以下类:[/color]
[b]AddFriendPacket[/b]              添加好友类       <- OutPacket
添加普通好友

[b]AddFriendReplyPacket[/b]         添加好友回复类   <- InPacket
添加普通好友回复,可能是添加成功,需要验证或者拒绝

[b]AddFriendAuthPacket[/b]          添加认证好友类   <- OutPacket
添加一个需要验证的好友,发出添加请求,通过好友的验证请求,以及拒绝请求

[b]AddFriendAuthReplyPacket [/b]    添加认证好友回复类   <- InPacket
这个是发送请求或者拒绝请求的回复, 成功表示服务器接受成功,可以等待好友回复了。
至于好友是否同意,那是系统提示类(SystemNotificationPacket)通知你的

[b]DeleteFriendPacket [/b]          删除好友类           <- OutPacket
删除一个自己列表里的好友

[b]DeleteFriendReplyPacket [/b]     删除好友回复类       <- InPacket
返回删除成功,或者失败

[b]DeleteMePacket[/b]               删除自己类           <- OutPacket
从好友的好友列表中删除自己, 大概就是“黑名单”的意思了

[b]DeleteMeReplyPacket[/b]          删除自己回复类       <- InPacket
这个是删除自己的回复,成功或者失败

[b]SystemNotificationPacket[/b]     系统提示类           <- InPacket
这个是当有人加你成功,请求验证,拒绝你的请求还有部分广告都
是通过这个包来通知你的



[color=red]evafriend.cpp evafriend.h 好友列表的解析和发送
包含以下类:[/color]

[b]FriendItem[/b]  一个好友项
包括好友的:qq号,头像,年龄, 性别, 昵称, 一些想是否有QQShow,摄像头
等等的标致

[b]GetFriendListPacket[/b]    获取好友列表请求       <- OutPacket
一个请求下载好友列表的类

[b]GetFriendListReplyPacket[/b] 获取好友列表请求回复 <- InPacket
这个回复里含若干个好友项(FriendItem), 包装成了一个
std::list<FriendItem> 结构。



[color=red]evafriendlist.cpp evafriendlist.h 好友列表的统一处理类
包含以下类:[/color]
[b]QQFriend[/b]  封装了一个好友所有有关信息的类
包括在哪个组(注意这里只放了组的索引号),IP地址,端口号,在线状态什么的,很多信息
[b]FriendList[/b] 一个好友列表的封装
用户可以通过这个类来对好友及全部列表进行操作。



[color=red]evagroup.cpp evagroup.h 好友分组的下载,上传
包含以下类:[/color]
[b]GroupNameOpPacket[/b]   上传和下载好友列表名的请求类          <- OutPacket

[b]GroupNameOpReplyPacket[/b]  上传和下载好友列表名请求的回复类  <- InPacket
注意,这里的回复组名不包括第一个默认的“我的好友”组名

[b]DownloadFriendEntry[/b]     一个好友分组的解析封装类
包括好友QQ号码, 好友类型(是否是群), 好友的分组索引号,这个
索引号对应下载的组名的次序,注意的是0是默认的“我的好友”,下载的
组名是从索引号1开始的

[b]DownloadGroupFriendPacket[/b]  下载好友分组信息的类           <- OutPacket
一个下载好友分组的请求

[b]DownloadGroupFriendReplyPacket[/b] 下载好友分组信息的回复类   <- InPacekt
把好友分组放入了一个std::list<DownloadFriendEntry> 结构中



[color=red]evaimreceive.cpp evaimreceive.h 接收好友发送信息的类
包含以下类:[/color]
[b]ReceiveIMPacket[/b] 一个初步解析好友发来的信息的类            <- InPacket
这个类会解析出发送者的QQ号,接收者的QQ号, 聊天的序列号,
发送者的IP, 发送者的端口,以及发送的类型,比如系统消息,一般
文本聊天消息,群聊天的文本消息
注意,消息的内容这个类不做处理,提供了原始数据,供相应
的类来处理

[b]ReceivedSystemIM[/b] 系统消息类
从ReceiveIMPacket得到原始数据,解析系统消息,比如你的号码在
其他地方登录,你被迫下线等等

[b]NormalIMBase[/b] 一个一般消息的父类   
从ReceiveIMPacket得到原始数据,解析一般消息, 得到一般消息的类型,
但是不具体解析内容,包括:发送者的客户端版本, 发送者QQ号,接受者QQ号,
文件密钥,消息类型(文本消息还是发送文件相关的请求),消息序列号,发送时间,
发送者头像,等等注意,这个类一样事实上就是判断一下类型,然后
交由其他继承这个类的类来统一解析

[b]ReceivedNormalIM[/b] 文本消息的解析类           <- NormalIMBase
从NormalIMBase得到原始数据,解析一般文本消息内容。
包括:是否是自动回复消息, 消息的GB编码的内容, 字体属性
注意,这里已经把消息中含有的系统表情转换为文本描述了

[b]ReceiveIMReplyPacket[/b] 一个好友发送信息的回复类    <- OutPacket
就是ReceiveIMPacket原始包的前16个字节



[color=red]evaimsend.cpp evaimsend.h 处理发送消息的类
包含以下类:[/color]
[b]SendIM[/b]  所有发送类的父类                       <- OutPacket
[b]SendTextIMPacket[/b] 文本消息发送类                <- SendIM
负责,填充文本消息的格式,包括,字体,字号,颜色信息,
是否自动回复,消息内容
[b]SendIMReplyPacket [/b]      发送消息的回复类       <- InPacket
服务器发回的,来通知是否发送成功的包



[color=red]evalogin.cpp evalogin.h   用来处理登录,登出,和在线操作的类
包含以下类:[/color]
[b]LoginPacket[/b]  登录类                      <- OutPacket
发出一个登录请求

[b]LoginReplyPacket[/b]  登录回复类             <- InPacket
收到服务器登录请求回复,回复信息包括
登录成功,服务器转向, 密码错误等等

[b]LogoutPacket[/b]  登出的类                   <- OutPacket
登出自己。 服务器不会回复这个包

[b]KeepAlivePacket[/b]   保持在线类             <- OutPacket
发出保持在线包,每隔一定时间需要发这样一个包

[b]KeepAliveReplyPacket[/b] 保持在线回复类          <- InPacket
里面含有自己的IP,端口,以及当前腾讯服务器
总登录人数



[color=red]evaonlinestatus.cpp evaonlinestatus.h 获得在线好友和改变在线状态的类
包含以下类:[/color]
[b]FriendOnlineEntry[/b]  解析一个在线用户的信息的类

[b]GetOnlineFriendsPacket[/b] 请求在线用户列表类                <- OutPacket

[b]GetOnlineFriendReplyPacket[/b] 请求在线用户列表回复类        <- InPacket
这个类里含有一个std::list<FriendOnlineEntry>结构来存储
解析后的列表

[b]FriendChangeStatusPacket[/b] 好友更改状态类                   <- InPacket
当你的一个好友更改了状态(登录,登出,上线,隐身,离开,下线)时,
都会收到这个包。 注意的是,如果是隐身登录,好友的状态的类型是
“下线”, 隐身也一样。

[b]ChangeStatusPacket[/b]  更改自己状态的类               <- OutPacket
包括4中状态(上线,隐身,离开,下线),注意下线的效果和登出是
一样的, 但是下线服务器会返回一个是否成功的包。

[b]ChangeStatusReplyPacket[/b] 更改自己状态的回复类            <- InPacket
返回成功,或者失败



[color=red]evauserinfo.cpp evauserinfo.h 处理用户资料的类
包含以下类:[/color]
ContactInfo  用户资料的封装类

[b]GetUserInfoPacket[/b]  获取用户资料的请求类         <- OutPacket

[b]GetUserInfoReplyPacket[/b]   获取用户资料的请求回复类         <- InPacket
调用ContactInfo来解析好友资料

[b]ModifyInfoPacket[/b] 更改个人资料类                <- OutPacket

[b]ModifyInfoReplyPacket[/b] 更改个人资料回复类       <- InPacket
更改成功或者失败

[color=red]evautil.cpp evautil.h 一些 libeva 用到的算法等
包含以下类:[/color]
[b]EvaUtil[/b] 提供了md5,两次md5,表情码和文本描述,及文件索引号的转换



[b]libeva.h[/b]  一个 libeva 头文件的封装
用户只要 include 这个头文件就可以了



[b]md5.c md5.h[/b]  做MD5处理的c源文件
取自 Gaim, 作者:L. Peter Deutsch  ghost@aladdin.com
版权信息参见改文件



[b]qq_crypt.c qq_crypt.h[/b] 处理 qq 加密,解密的源文件
取自 openq 项目, 作者:Puzzlebird
(注:加入了c++支持)


同时附上 libeva 源文件

[color=blue]--------------------------------------------
排版:bamfox , 请 yunfan 核查,谢谢。[/color]

yunfan 发表于 2005-1-6 16:03

说明一下, libeva中所有的std::string 类型都是GB或者/GBK 类型编码。
使用时,需要做内码转换, 建议用GBK内码转化。

比如在Qt中的QString是使用Unicode编码的, 那么可以这么来转换

假定消息是: std:string msg;

那么做下面的转换才可以在大多数系统上正常显示:
QTextCodec *codec = QTextCodec::codecForName("GBK");
QString unicodeMsg = codec->toUnicode(msg.c_str());

如果要从QString 转为GBK 编码的 std::string, 可以这么做
假定消息是 QString unicodeMsg;
QTextCodec *codec = QTextCodec::codecForName("GBK");
std::string = (codec->fromUnicode(unicodeMsg)).data();

希望大家注意就好。

cjacker 发表于 2005-1-6 16:09

够快,support!!!!!!!!!!!

cjacker 发表于 2005-1-6 16:16

你个没人性的:-D

连个工程文件都不给了:-D


wait中,等你这块结束了,我就port一些kopete plugin

yunfan 发表于 2005-1-6 16:19

[quote:747542fd52="cjacker"]你个没人性的:-D

连个工程文件都不给了:-D


wait中,等你这块结束了,我就port一些kopete plugin[/quote]

呵呵, 还没做完呢
快了,快了

casper 发表于 2005-1-6 16:47

好东西,正在看libeva的源代码,有了上面的东西,就可以有个整体的印象,只是src里面的注释太少了,对于我这样水平的人,读起来还是比较头大 :oops:

yunfan 发表于 2005-1-6 16:50

[quote:9e0d9a6e4b="casper"]好东西,正在看libeva的源代码,有了上面的东西,就可以有个整体的印象,只是src里面的注释太少了,对于我这样水平的人,读起来还是比较头大 :oops:[/quote]

不明白的地方,就跟贴问,我回答,也可能,我哪里处理的不好也说不定的。

caihua 发表于 2005-1-6 16:52

:roll: [b]cjacker[/b]你今天下午来我们公司了 :?:

zongtongyi 发表于 2005-1-6 17:19

:mrgreen:  :lol:

bamfox 发表于 2005-1-6 17:29

[quote:1ceb5ce53b="casper"]好东西,正在看libeva的源代码,有了上面的东西,就可以有个整体的印象,只是src里面的注释太少了,对于我这样水平的人,读起来还是比较头大 :oops:[/quote]

我跟你一样一样哇,一起学习。如果有兴趣的话,我们就把程序流程图画出来吧 :D

yunfan 发表于 2005-1-6 17:53

呵呵, 我说怎么一下变的好看了. bamfox 多谢!

atfa 发表于 2005-1-7 08:02

yunfan基本是个好同志 :mrgreen:

呵呵开玩笑了

我们领导前天吃饭喝酒的时候,喝得半醉说:“XX基本是个好同志”

大家哄堂大笑,结果这几天大家不管说什么都把“基本”二字挂在嘴边 :mrgreen:

嗯!yunfan基本是个好同志 :mrgreen:

VimChina 发表于 2005-1-7 08:51

我也学学啊,是不是这么说:
[quote][b]atfa[/b]基本是个好淫[/quote] :mrgreen:

cjacker 发表于 2005-1-7 09:27

[quote:33afddf059="caihua"]:roll: [b]cjacker[/b]你今天下午来我们公司了 :?:[/quote]
没有阿。来了我能不去看你?

caihua 发表于 2005-1-7 10:49

:mrgreen: 我还以为昨天下午那个戴眼睛的是你咧 :oops:

yunfan 发表于 2005-1-7 12:38

一个重要的更正, 是我的错误所致, 可能在有些系统上不能编译,会有如下编译错误
[quote]
evapacket.cpp: In member function `int OutPacket::putHead(unsigned char*)':
evapacket.cpp:33: error: `int Packet::qqNum' is private
evapacket.cpp:238: error: within this context
[/quote]

如果出现这个问题,请大家做如下更改:
把 evapacket.cpp 文件的 238 行:
int id = htonl(qqNum);

改为
int id = htonl(getQQ());

这个错误的原因是
int qqNum 是 Packet 类的私有变量
而我在它的继承类OutPacket里调用了这个变量,只应该是不可以的,但是不知道
为什么我的编译器没有报错。

而本来我是把qqNum 放在 protected: 里的, 继承类是可以访问的, 后来改掉了。


多谢Jacky Gu <jacky-gu@163.com> 给我的反馈
给大家带来的不便望,大家包含

llemmx 发表于 2005-1-9 20:14

在看代码的时候发现在kdevelep中看中文注释是乱码后来没辙跑到kwrite里看才基本正常但仍然有乱码.其他人有遇到么?????
"/** 这是文件传输时的数据消息包开头字芄17*/"

yunfan 发表于 2005-1-9 20:56

[quote:443e23ca20="llemmx"]在看代码的时候发现在kdevelep中看中文注释是乱码后来没辙跑到kwrite里看才基本正常但仍然有乱码.其他人有遇到么?????
"/** 这是文件传输时的数据消息包开头字芄17*/"[/quote]

新版除了 evadefines.h里有中文,其他文件都是英文得,不应该有乱码。
如果出现乱码, 用UTF8编码看。 我都是用UTF8存得。

另,以前版本改lumaqq的, 很乱,个别可能会有乱码, 那些基本都是lumaqq里的注释,我拷过来了, 内码转换的时候可能有点问题。希望还是主要看现在的测试版里的文件,可以参考以前的。

llemmx 发表于 2005-1-9 21:34

今晚总算腾出时间来仔细研究代码了.后面会贴一些代码的修改建议.
source file name:evapacket.cpp
Line:46
Packet::Packet(unsigned char *buf, int *len)
{
        int pos;
        // if it's not UDP, must be TCP
        if(!mIsUDP)       
                pos = 3;
        else
                pos = 1;
       
        version = buf[pos]*0x100+buf[++pos];
        command = buf[++pos]*0x100+buf[++pos];
        sequence = buf[++pos]*0x100+buf[++pos];
       
        (*len)=(*len)-pos;
        memcpy(buf, buf+pos, *len);
}

yunfan 发表于 2005-1-9 22:06

[quote:a90b8d4336="llemmx"]今晚总算腾出时间来仔细研究代码了.后面会贴一些代码的修改建议.
source file name:evapacket.cpp
Line:46
Packet::Packet(unsigned char *buf, int *len)
{
        int pos;
        // if it's not UDP, must be TCP
        if(!mIsUDP)       
                pos = 3;
        else
                pos = 1;
       
        version = buf[pos]*0x100+buf[++pos];
        command = buf[++pos]*0x100+buf[++pos];
        sequence = buf[++pos]*0x100+buf[++pos];
       
        (*len)=(*len)-pos;
        memcpy(buf, buf+pos, *len);
}[/quote]

不错,不错,提的好,我说一下我的想法啊。

我考虑,这种方法不如直接位操作快,以前我是位操作的,
但后来觉得因为大家的系统不一样,字节序也可能不一样
(就是big endian和little endian的问题), 为了避免这样的问题
就用了ntol/ntos函数族, 腾讯协议里凡是int,short的发过来都是
big endian的, 这样可以保证字节序最大程度不出错。

这个问题大家可以讨论,讨论 :lol:

llemmx 发表于 2005-1-9 22:07

看来我还得翻翻lumaqq的代码了,这里发现在evapacket.cpp 中分配内存使用了malloc很奇怪哦,既然是cpp为啥不用new??还有这里fprintf函数是否是用来在控制台上显示错误信息的?(我不是特别清楚)如果是的话我记得该函数有缓存溢出的可能.(我希望代码能更坚固一些^0^)
source file name:evapacket.cpp
Line:217
int OutPacket::putHead(unsigned char *buf)
{
        int pos=0;
        if(!isUDP()){
                buf[0]=buf[1]=0;
                pos+=2;
        }
        buf[pos++]=(unsigned char)QQ_PACKET_TAG;
       
        buf[pos]   = (version >> sizeof(unsigned char)) & 0xFF;
        buf[++pos] = version & 0xff;
       
        buf[++pos] = (command >> sizeof(unsigned char)) & 0xFF;
        buf[++pos] = command & 0xff;

        buf[++pos] = (sequence >> sizeof(unsigned char)) & 0xFF;
        buf[++pos] = sequence & 0xff;
       
        int id = htonl(getQQ());
        memcpy(buf+pos, &id, 4);
        pos+=4;
               
        return pos;
}

llemmx 发表于 2005-1-9 22:18

[quote:31eaf4613e="yunfan"]

不错,不错,提的好,我说一下我的想法啊。

我考虑,这种方法不如直接位操作快,以前我是位操作的,
但后来觉得因为大家的系统不一样,字节序也可能不一样
(就是big endian和little endian的问题), 为了避免这样的问题
就用了ntol/ntos函数族, 腾讯协议里凡是int,short的发过来都是
big endian的, 这样可以保证字节序最大程度不出错。

这个问题大家可以讨论,讨论 :lol:[/quote]
也就是说QQ里协议传输时都是顺序字节传输的是吧.
其实我这样处理一般只适合32位机器,我们是否在网上声明eva是for x86的32位软件呢?因为我看见我们在代码上好象不支持64或16位系统哦.

yunfan 发表于 2005-1-9 22:28

[quote:dc115896c6="llemmx"]
也就是说QQ里协议传输时都是顺序字节传输的是吧.
其实我这样处理一般只适合32位机器,我们是否在网上声明eva是for x86的32位软件呢?因为我看见我们在代码上好象不支持64或16位系统哦.[/quote]

这个我不知道, 你是否说32位的系统都是这种逆字节序的?

llemmx 发表于 2005-1-9 22:33

哦对了,我们在解析协议时能否能使用先解析在使用的方法呢?就是说我们将协议描述成一个一个的struct
每个协议来到时我们先将数据解包到这些struct中
在解析的过程中可以对这些数据进行合法性检查.成功了在正真使用这些数据,可能会慢点但是应该安全.我在公司里与那些个单片机的实时操作系统通讯时都用的这方法.
例如:
02 03 04 05 06 BB
假如这里我们的0203,0405,06BB在单片机中表示整形
那么我们pc机的结构体可以定义成
struct{
    int nVar1;
    int nVar2;
    int nVar3;
}ss;
缓冲区的名称为buf
解析
ss.nVar1=buf[0]*0x100+buf[1];
...
如果这个包没有任何问题我们将直接提交结构体处理.(这里是c的例子如果是c++的话那么就可以用类来代替结构体那么直接提交的是类)
我是不是太唠叨了 :mrgreen:

llemmx 发表于 2005-1-9 22:34

不,只要是x86系统的pc机都这样从16为到64位均如此

llemmx 发表于 2005-1-9 22:38

可能是个人编程的毛病.由于本人老写服务器所以对代码的细节注意太多了.所以大家看了以后做参考就可以了,最好还是考虑当前的环境在定.别给细节打倒了.

yunfan 发表于 2005-1-9 22:50

[quote:8843325cae="llemmx"]哦对了,我们在解析协议时能否能使用先解析在使用的方法呢?就是说我们将协议描述成一个一个的struct
每个协议来到时我们先将数据解包到这些struct中
在解析的过程中可以对这些数据进行合法性检查.成功了在正真使用这些数据,可能会慢点但是应该安全.我在公司里与那些个单片机的实时操作系统通讯时都用的这方法.
例如:
02 03 04 05 06 BB
假如这里我们的0203,0405,06BB在单片机中表示整形
那么我们pc机的结构体可以定义成
struct{
    int nVar1;
    int nVar2;
    int nVar3;
}ss;
缓冲区的名称为buf
解析
ss.nVar1=buf[0]*0x100+buf[1];
...
如果这个包没有任何问题我们将直接提交结构体处理.(这里是c的例子如果是c++的话那么就可以用类来代替结构体那么直接提交的是类)
我是不是太唠叨了 :mrgreen:[/quote]

:lol: , 不唠叨。
你再看看我程序的结构, 把你的想法再具体一点, 我也考虑这样可能会出现
解析问题, 但是我使用中没有发现出过问题。另外,因为我知道接收到了多少
个字节,字节不符条件,就不解析了,我考虑在父类Packet里加一个标识解析出错的变量, 用解析过的包的时候先看是否出错。

你再考虑一下你上面的想法,把它落实到Eva上,开发人员再一起探讨一下,
如果可行,我们下一版就改掉,这一版先这么做出来,再完善它。

呵呵,慢一点没关系, 数据有没有多少, c++的也慢不到那里去的。
不会有明显的感觉的。

yunfan 发表于 2005-1-9 22:59

[quote:8e977fdf88="llemmx"]可能是个人编程的毛病.由于本人老写服务器所以对代码的细节注意太多了.所以大家看了以后做参考就可以了,最好还是考虑当前的环境在定.别给细节打倒了.[/quote]

对技术上的东西,就该精益求精  :D

wulie88 发表于 2008-3-9 15:16

刚来的。

有没有完整的开发说明,虽说我底层的数据传输现在还不是很了解。

但是我可以重新写一个结构,改善一下外观和功能啊。

请版主把信息发送到[email]wulie88@gmail.com[/email]

页: [1]

Powered by Discuz! Archiver 6.1.0F  © 2001-2007 Comsenz Inc.