ascs 简明开发教程(十五):如何编写解包器

31 篇文章 0 订阅
31 篇文章 1 订阅

QQ交流群:198941541

解包器必须从ascs::i_unpacker继承,i_unpacker接口定义如下:

template<typename MsgType>
class i_unpacker
{
public:
	typedef MsgType msg_type;
	typedef const msg_type msg_ctype;
	typedef std::list<msg_type> container_type;
	typedef ASCS_RECV_BUFFER_TYPE buffer_type;

	bool stripped() const {return _stripped;}
	void stripped(bool stripped_) {_stripped = stripped_;}

protected:
	i_unpacker() : _stripped(true) {}
	virtual ~i_unpacker() {}

public:
	virtual void reset() = 0;
	virtual void dump_left_data() const {}
	//heartbeat must not be included in msg_can, otherwise you must handle heartbeat at where you handle normal messages.
	virtual bool parse_msg(size_t bytes_transferred, container_type& msg_can) = 0;
	virtual size_t completion_condition(const asio::error_code& ec, size_t bytes_transferred) {return 0;}
	virtual buffer_type prepare_next_recv() = 0;

	//this default implementation is meaningless, just satisfy compilers
	virtual char* raw_data(msg_type& msg) const {return const_cast<char*>(msg.data());}
	virtual const char* raw_data(msg_ctype& msg) const {return msg.data();}
	virtual size_t raw_data_len(msg_ctype& msg) const {return msg.size();}

private:
	bool _stripped;
};

有些接口已经有了默认实现,可能原因是它们不是必须的,注意默认实现并不一定有意义,只是为了让不使用它们的用户,在不重写它们时,也能通过编译而已,下面我们来详细介绍每一个接口:

bool stripped() const {return _stripped;}
void stripped(bool stripped_) {_stripped = stripped_;}

是否剥离消息,即返回的消息,是否包涵协议部分,默认为剥离消息(即不包涵协议部分),当然这只是我的建议,因为最终解包器是用你来写,你也可以不遵守这个约定,ascs并不关心这个属性。

virtual void reset() = 0;

在对象被重用时,解包出错时由框架调用,所有解包器都会有自己的成员变量,所以这个函数必须重写,还得好好写,否则可能造成你的解包器在解包出错之后再也解不出新消息来。

virtual void dump_left_data() const

在on_unpack_error里面由框架调用,推荐的功能是打印所有未解出消息的数据便于调试,可以不重写。

virtual buffer_type prepare_next_recv() = 0;

在开始数据读取之前,asio需要一个缓存对象,比如asio::mutable_buffer,你必须返回一个有效的缓存,且其生命周期(真正缓存的生命周期,不是缓存对象)必须持续到 parse_msg调用之后。

virtual size_t completion_condition(const asio::error_code& ec, size_t bytes_transferred)

是否结束数据读取(并开始消息解析——调用parse_msg),返回0代表结束(你应该至少收到了一个完整的消息才返回0,这里说至少是考虑到粘包问题,可能一次读取就能得到多个消息,但你也不能故意等到读取到多个消息之后才返回0,这样会让前面的消息得不到及时的处理),非0表示在下次调用completion_condition之前,最多可以读取的数据字节数(可以返回一个非常大的值而不用考虑缓存溢出,asio会先保证缓存的安全,其次再参考你提供的读取大小),下次调用completion_condition的时候,库会告诉总共读取的字节数(bytes_transferred),在completion_condition返回0之前,所有回调completion_condition里面的bytes_transferred都累加的,返回0之后bytes_transferred将归零0并重新累计(这是asio的设计)。库会先调用prepare_next_recv,然后调用一次completion_condition,显然这次调用里面的bytes_transferred为0(归零0并重新累计),解包器返回一个最多可以读取的数据字节数,然后asio开始异步读取数据(调用socket的async_read_some),这一读会得到两个结果,一个是缓存(prepare_next_recv返回的缓存)还没有写满,此时asio将再次调用completion_condition,由解包器来决定是继续读(返回大于0)还是结束读(返回0),继续读即重新上面的过程,结束读的话,库会结束这次async_read并开始解包(调用解包器的parse_msg);另一个是缓存已经写满,此时在低版本asio下面,asio仍然会再调用一次completion_condition(多余),此时completion_condition显然应该返回0(你的解包器应该考虑这种情况),如果在高版本asio下面,asio不会再次调用completion_condition,而是直接结束async_read,然后库开始解包(调用解包器的parse_msg)。建议你的解包器支持这两种asio行为,所以在写解包器的时候,头脑中保持一点,有可能completion_condition并没有被调用(第一次除外)就直接到了parse_msg了,如果你在completion_condition(非第一次)里面做了一些逻辑,一定要在parse_msg里面也有相应的逻辑。举个例子,你在completion_condition里解了包头(假设包头是长度)以确定什么时候返回0(结束这次async_read并开始解包),并将消息长度保存在解包器的一个成员变量里面,这个逻辑显然在第一次completion_condition里面是做不了的(还没有接收到任何数据),于是你在非第一次的completion_condition里面解包头,然后在parse_msg里面就可以直接得到消息长度了,可是在高版本asio下面,你在parse_msg里面得到的消息长度可能就不正确了(仅是一个初始值),这是因为asio只调用了一次completion_condition(缓存就被写满了),你在completion_condition里面的逻辑(非第一次)并没有运行。

virtual bool parse_msg(size_t bytes_transferred, container_type& msg_can) = 0;

真正的解包函数,多个消息通过容器返回。bytes_transferred就是返回0的那一次completion_condition调用里面的bytes_transferred。如果解包出错,请返回false(然后ascs将调用你重写的reset接口和dump_left_data接口,其中后者是在on_unpack_error里面调用的,如果你重写了它但仍然需要调用dump_left_data接口,就需要你自己调用,或者在on_unpack_error里面调用父类同名函数),注意对于解包出错之前已经解出来的消息,仍然可以通过msg_can返回,ascs仍然会为你派发这些消息。

char* raw_data(msg_type& msg) const
const char* raw_data(msg_ctype& msg) const
size_t raw_data_len(msg_ctype& msg) const

从一个解包好的消息里面,得到真正的数据部分(排除协议数据),不是任意协议都可以实现这三个接口(不拷贝数据的情况下),框架不会使用它们,放在这里只是推荐你去实现(如果可能的话)。

 

对于心跳消息,解包器可以返回也可以直接过滤掉,都不影响ascs对于心跳超时的判断,如果你选择返回,那在消息派发时(比如on_msg_handle),你要能够排除掉它。

所有接口都无需考虑多线程安全的问题(除了最后3个,ascs并不使用它们,所以线程安全要看谁使用它们,怎么使用它们)。

解包器必须考虑分包粘包问题,是一个很容易出错和出性能瓶颈的地方,请小心对待,特别是分包,你需要保存残留数据,并拼接在下次接收数据的前面,可以参考我默认提供的那几个解包器。

不同于打包器,运行时替换解包器是比较麻烦的,因为解包器有自己的成员变量(比如缓存),替换不好会造成ascs引用到非法地址,我们将在下一篇里面专门讲运行时替换解包器。

上一篇:ascs 简明开发教程(14) 下一篇:ascs 简明开发教程(16)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Universal Extractor,是一个“万能解包工具”。它能解开现在流行打包工具比如:NSIS,Inno 等等!无论是简单的 Zip 文件,还是一个安装程序,甚至连 Windows Installer (.msi) 程序包,它也能轻松自如地提取出其中的文件。它的存在,无心与WinRAR、7-Zip抗衡,更多的,是给予我们一种简单之美,一种便捷之爽……有了它,软件的世界不再有“笑里藏刀”;有了它,每个人都拥有了一双“火眼金睛”;有了它,无论是杀人灭口还是居家旅行,我们都多了一分无比的舒心;有了它…… 1.6 相对于1.5的更新(02222007):  增加使用TrID来进行文件类型检测; 判断文件格式不再依赖扩展名  增加支持 Adobe Reader 7.x 和 8.x 安装程序  增加使用uudeview来支持 Base64, Quoted-Printable, UUencoded, 以及 yEnc 文件  增加使用cmdTotal + InstallExplorer来支持 Gentee, 安装程序 VISE, 和 SetupFactory 安装程序  增加使用stix来支持 InstallShield 3.x SFX 安装程序  增加使用i5comp支持 InstallShield 5.x CAB 文件  增加支持 Itsy 包 (.ipk)  增加使用cmdTotal + DBX 插件来支持 Outlook Express (DBX) 档案  增加支持 Pea 档案  增加使用cmdTotal + PDunSIS支持 SIS (SymbianOS) 安装程序  增加支持 StuffIt packages (.sit, but not .sitx)  增加使用7-zip 和 cmdTotal + MSI 插件支持 Windows Installer 补丁(.msp) 文件  增加使用booz支持 Zoo 档案  增加支持无扩展名文件(例如, 未知文件)  安装程序增加 noappendext, noremovedupe, noremovetemp 参数  安装程序增加对.dbx, .msp, .pea, 和 .sit 文件关联  增加波兰文、俄文、斯洛伐克文语言文件  修复不能释放 UPX 加壳过的 Zip 和 7-Zip 安装程序的BUG  修复 Inno Setup 安装程序检测方面的问题  修复某些安装程序(主要是 InstallShield)中的潜在的文件输出BUG  修复ACE SFX 处理程序为解压失败继续处理  修复InstallShield 缓存模式处理程序挂起的问题  移除了 InstallShield cab文件的“i6comp 按组”选项  移除了 expand.exe 以利于使用 7-Zip  从安装程序移除了.in_, .oc_, .sr_, .1, .bin, .imf, .lib, 和 .wz 文件关联  更新 UniExtract 为默认扫描文件类型签名; 扩展名用作替补判断  更新 UniExtract 释放文件时在系统托盘处显示图标  更新 UniExtract 的PEiD 和log提示窗口默认选择“取消”按钮  更新 UniExtract 国际化支持简单翻译,减小了语言文件大小  更新 UniExtract 增加许多代码,以便可能时增加健壮性、可用性和效率  更新 使用cmdTotal + InstallExplorer支持 Inno Setup, NSIS, Wise 安装程序, 和 InstallShield  更新 ACE 支持,包括 非PE ACE SFX 档案  更新 BINCUE 支持,更好的转换和验证  更新 InstallShield 3.x 档案支持,增加 STIX 释放选项  更新 InstallShield CAB 支持以显示进度对话框  更新 InstallShield 缓存释放以支持多重嵌套的MSI  更新 InstallShield 缓存释放以更好检测安装程序窗口  更新 InstallShield 支持以提供选择 isxunpack 或缓存模式  更新 MHTML 支持以包括通过 cmdTotal + MHT Packer 插件进行释放的选项  更新 Wise 安装程序支持以包括 非PE 的 Wise 安装程序  更新 Wise 安装程序支持以包括 unzip 选项  更新 Wise 安装程序支持以支持增补丢失的文件扩展名  更新 Wise 安装程序支持以提供移除临时文件的选项  更新 Microsoft 自释放 CAB 支持,更好的检测和强健性  更新 Windows Installer 支持以支持增补丢失的文件扩展名  更新 Windo

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值