到目前为止,我已经介绍了QQProtocol中代码的基本结构:
CQQUser存储一个QQ用户的所有信息,包括客户指定信息(用户名,密码等)和服务器返回信息(指在和服务器通讯过程中服务器“设置的”信息,如各种各样的密钥是在和服务器通讯过程中某些特定步骤由服务器生成并返回的,而还有些信息如服务器IP和端口则是最初由客户指定,但有可能因为服务器重定向而改变,等等等等)
CInPacket和COutPacket是发送/接收包基类,CBasicInPacket/CBasicOutPacket是QQ基本协议族发送/接收包基类,CBasicFamilyParser是QQ基本协议族消息管理器,至于要和QQ服务器正常通讯,还要用“翻译”成QQ服务器的“语言”——由CCrypter加密/解密包
最后,所有类使用的常量则实现为类QQ的公共静态成员变量
而所有这些类都是为CQQClient类服务的,它是QQProtocol的操作接口(数据接口则是CQQUser)。目前实现的版本主要接口是Login,负责向QQ服务器登录。由它负责创建一个工作线程,向服务器发包,等待回复,回复的结果调用CQQClient的虚函数,所以对于库的使用者,需要生成一个派生类实现这些虚函数以处理回复。
至此,很多朋友关心的demo也可以实现一个最简单的版本了,目前的demo是个命令行程序,使用UDP方式登录,已经测试成功。
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/例子svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/demo/引用请注明出处,谢谢
posted @
2007-10-31 10:38 叶子墙 阅读(5003) |
评论 (14) |
编辑 收藏
有了请求登录包返回的密钥(CQQUser::m_pbInitKey),就可以开始构造登录包了(这里顺便提一下,登录包里带有QQ最重要的数据——密码,没有登录密钥直接发送密码原文是极不安全的,这也是为什么登录包之前还要请求登录/应答过程的原因)。
登录和登录回复包是用登录密钥加密/解密的
登录包构造(CLoginPacket::PutBody)和登录回复解析(CLoginReplyPacket::ParseBody)的每一步在源代码中注释都有说明,其中着重注意:
登录包构造中:没什么好说的,看代码
登录回复包解析中:
很多请况下不会直接登录成功,而是由服务器返回一个登录重定向包(回复码是QQ.QQ_REPLY_LOGIN_REDIRECT),而且不止一次重定向服务器,所谓重定向即——你向一个QQ服务器发送登录请求,而这个服务器会告诉你另一个QQ服务器地址和端口(不要告诉我你认为腾讯只有一个服务器
),这时你就要向后者重发登录包(但是登录密钥都是一样的)。至于为什么不直接在这个服务器登录,这你就要问腾讯的客服了(取决于服务器的策略)
如果登录失败,会返回失败消息的字符串
最后就是登陆成功的消息,登录成功会返回很多重要信息,包括你想到的和没想到的,其中session key(会话密钥)是下一个密钥,你今后发送的大多数消息和接收的大多数消息,都是用这个密钥加密解密的
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/引用请注明出处,谢谢
posted @
2007-10-26 23:20 叶子墙 阅读(3060) |
评论 (5) |
编辑 收藏
下面我们开始接触基本协议族具体的发送/接收包,所有的QQ会话都开始于客户端发送基本协议族的请求登录包,这个包的包体是空的,但是在包头因为有请求登录的QQ号(实际上正如前面介绍的,每个基本协议族包的包头都带有QQ号),服务器会知道是哪个QQ号请求登录
QQ服务器返回应答,应答的包体是明码传输的(因为现在还没有密钥),包括回复码,如果允许这个号码登录则回复码是QQ.QQ_REPLY_OK,而且返回一个登录令牌,这个登录令牌是在整个QQ会话过程中的第一个密钥
在QQProtocol中请求登录包是CBasicOutPacket派生类CRequestLoginTokenPacket实现的;而其回复包是CBasicInPacket派生类CRequestLoginTokenReplyPacket实现的。
正如在基本协议族消息管理器中介绍的一样,请注意CBasicFamilyParser代码改变了
而且因为CRequestLoginTokenReplyPacket要直接访问CQQUser的非公开成员,所以CQQUser代码中CRequestLoginTokenReplyPacket被设为CQQUser的友元
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/引用请注明出处,谢谢
posted @
2007-10-20 01:37 叶子墙 阅读(1793) |
评论 (0) |
编辑 收藏
CBasicFamilyParser处理基本协议族的收发包,它的代码很简单,但作用很重要,它对基本协议族的支持包括:
要发送包的只要传进来一个CQQUser对象(由这个对象提供要发送包的参数),指定命令号,就会生成一个CBasicOutPacket派生类对象,而这个CBasicOutPacket派生类对象会送到发送队列中,由发送队列统一调配发送时间,决定需不需要重发,收没收到反馈等等,发送队列以后再详细说明,这里只提到发送一个包并不是直接做一个二进制buffer,通过socket发出去的,而是生成一个CBasicOutPacket派生类对象,提交给发送队列,发送队列会对所有CBasicOutPacket派生类对象统一管理,决定每个包的发送时间,重发次数,以及收到Ack删除对应的包等等
收到的原始数据(二进制数据)通过它转换为CBasicInPacket派生类对象,这个CBasicInPacket派生类对象根据不同的包类型有不同的用途,比如向上层报告收到的聊天消息,好友状态变更,系统消息,或者是发送包的Ack包,则提交给发送队列,让发送队列删除收到Ack包的发送包(否则这个包会重发)
CBasicFamilyParser类接口非常简单,对外有CreateOutPacket生成发送包对象和ParseInPacket生成接收包对象。这个类在每引入一对发送/接收包都会改变,由于目前还没有介绍一个CBasicIn/OutPacket派生类,所以目前的CreateOutPacket和ParseInPacket都是空的
比如请求登录包/应答包是CRequestLoginTokenPacket/CRequestLoginTokenReplyPacket,那么CreateOutPacket代码中会有
if( sCommand == QQ.QQ_CMD_REQUEST_LOGIN_TOKEN )
{
pBasicInPacket = new CRequestLoginTokenReplyPacket(pQQUser);
}
而ParseInPacket代码中会有
if( sCommand == QQ.QQ_CMD_REQUEST_LOGIN_TOKEN )
{
return new CRequestLoginTokenPacket(pQQUser);
}
依此类推
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/引用请注明出处,谢谢
posted @
2007-10-17 02:59 叶子墙 阅读(1376) |
评论 (0) |
编辑 收藏
QQ基本协议族包括大多数QQ系统消息和IM消息,除此之外,还有p2p协议族和04、05等等协议族
QQ基本协议族的接受包基类是CBasicInPacket,发送包基类是CBasicOutPacket,分别从接受/发送包基类CInPacket和COutPacket派生而来
CBasicOutPacket把COutPacket的m_bHeader置为QQ.QQ_HEADER_BASIC_FAMILY,实现了COutPacket的纯虚函数PutHeader和PutTail;CBasicInPacket实现了CInPacket的纯虚函数ParseHeader和ParseTail。也就是说基本协议族的包头和包尾有相同的结构,可以看一下接本协议族的包头结构和包尾结构
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/ 引用请注明出处,谢谢
posted @
2007-10-15 04:22 叶子墙 阅读(1614) |
评论 (3) |
编辑 收藏
接收包基类CInPacket,发送包基类COutPacket
QQ协议中,不管是接收包还是发送包,在应用层都分为三个部分:
包头,包括协议类型,源QQ版本号,命令号,和包序列号,发送包还有一个需要/不需要ack标志
包体和包尾,其中包体是需要加密解密的部分,以后再介绍
值得注意的是,QQ号码和密码等等都是在包体即需要加密解密的部分中,但包头中包含的东西非常重要,它包括:
QQ协议类型,如QQ基本协议族用的是QQ::QQ_HEADER_BASIC_FAMILY(CInPacket/COutPacket的m_bHeader)
源QQ版本号,在发IM包(即和其它QQ号通讯)中,在包体也会设置一次,但和QQ系统通讯时,这个源QQ版本号就是这个协议库使用的版本号,现在是QQ::QQ_CLIENT_VERSION_0E1B,即QQ2005的版本号(CInPacket/COutPacket的m_sSource)
命令号,这个就很多了,每个CInPacket/COutPacket派生类会设置这个值(CInPacket/COutPacket的m_sCommand)
包序列号,QQ用它来防止丢包,这个只是发IM包时用到,包序列过程以后介绍(CInPacket/COutPacket的m_sSequence)
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/引用请注明出处,谢谢
posted @
2007-10-12 00:27 叶子墙 阅读(1473) |
评论 (0) |
编辑 收藏
CCrypter类被实现为对QQ消息的加密解密,提供两个公共函数Decrypt和Encrypt。QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节。密钥在整个消息交互过程中会不同,以后会详细介绍,这里先提一下,有密码密钥,初始密钥和会话密钥,但是加密算法都是一样的。关于算法,代码上有详尽说明
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/引用请注明出处,谢谢
posted @
2007-10-08 00:22 叶子墙 阅读(2618) |
评论 (2) |
编辑 收藏
基于QQ协议的资料网上有很多了,lumaqq是非常著名的java实现的QQ客户端,本QQ协议库也是基于这些资料和少量抓包分析包后实现的。本协议库是基于QQ2005协议,全VC6实现
先贴出来QQ协议常量,基本上就是lumaqq完全移植的啦,所有协议常量都实现为类QQ的公共静态成员,给协议库中其它类使用,这么做完全是移植起来方便
class QQ
{
public:
...
};
CQQUser类:一个QQ用户的封装,如果一个QQ号码要登录,先要生成一个CQQUser对象,并进行设置
主要函数:
构造函数CQQUser 构造一个CQQUser对象时指定QQ号码和密码
SetServerIP 设置处理这个QQ用户的服务器,如sz.tencent.com
SetServerPort 端口,sz.tencent.com的端口是8000
SetHiddenLogin (是否)隐身登录
代码svn:
https://vcye23.svn.sourceforge.net/svnroot/vcye23/qqprotocol/
posted @
2007-10-06 01:51 叶子墙 阅读(11982) |
评论 (21) |
编辑 收藏