前文已经讲述,字母全排列是个惊人的数字,即使只遍历小写字母和数字6个全排列也有36^6 = 217678233621亿多个,7个排列36^7 = 78364164096783亿多,8个排列36^8 = 28211099074562.8万亿多个,数字非常惊人。Md5反查是个string-string的映射,16-N个字符的映射,如果考虑hex模式的md5那就是32-N的映射,考虑映射人们最先想到的可能都是数据库存储方式,我也首先想到了用数据库存储,分别考察了一下sqliteberkeleydb,但测试下来制造数据的速度很慢,sqlite加索引大概只能到5w条记录/s,不加索引为10w/sberkeleydb用单条模式大概只能到4.5w/s,这个速度已经很慢了,更难于接受的是如果写1000wsqlite加索引来说不是耗时200s,而是2000s了,也就是说耗时随单个数据文件记录的条数增多几乎成平方模式递增,而不是简单的线性递增,这是很要命的,就算制造1亿条数据耗时也是惊人,我的实测中没有测试过用sqlite制造1000w条以上的数据,在我心目中已经否定了那种模式。虽然我知道很多号称有多少亿条数据的网站其实都是用的数据库,我不知道他们花了多少时间制造数据,或者几天,或者几个月,或者更长时间,反正我对采用普通数据库模式制造数据完全持否定态度,嵌入式速度太慢,其他数据库则不光速度慢而且也不适合分布式应用,难道用户每装个点还要装个mysql之类的数据库,几乎不可能啊。

下面说说我的方法,我本来第一版本是计划先不做文件式数据库的,第一版本来只规划了做内存数据,充分榨取每一个字节,关于内存数据库我实现了好几个版本,下面分别介绍一下:

版本1hash模式

char key[16];做键,char pass[n];做内容,由于hash桶占用了一些字节:

                DWORD h, nKeyLen;               //hash键值, 字符串长度

                DWORD tag;                           //私有值,默认为0提供给外部使用

                bucket *pListNext;           //hash表双链的下一个节点

                bucket *pListPrev;           //hash表双链的上一个节点

                bucket *pNext;                        //拉链的下一个节点

                VALUE second;                        //具体数据

                _Elem first[0];                 //first

用这个hash模式大概存储一个6个字符的串的md5信息花了50个字节,花费太多,结果自然存不了多少数据,该方案作为第一验证方案,除了花费内存太多还是个能通过的方案。

 

版本2hash简化方案

在上述版本基础上简化桶设计,抛弃作为标准桶的一些字段,精简之后如下:

                DWORD h;                               //hash键值

                bucket *pNext;                        //拉链的下一个节点

                byte nKeyLen;                  //字符串长度

                VALUE second;                        //具体数据

                _Elem first[0];                 //first

该版本存储一个6个字符的串的md5信息需要31个字节,比版本1少了很多,进步一些了。

方案1和方案2速度都很快。

 

版本3vector方案

考虑到hash占用内存较多,采用vector方案,直接存储

Char mm[16];

Char pass[n];

存储一个6个字符的串的md5信息需要22个字节,该方案排序速度太慢,查找速度肯定也比不上版本1和版本2,之后还测试过将vector里面存储指针,那种模式每个6个字符的串的md5信息占用内存26个,接近hash版本,排序速度比直接存储数据的好一点,但也还是很慢,总之这个方案作为一个过度方案最终也被放弃了。

 

 

方案4:全文件Hash紧缩方案

以上这些方案的特点是都存储了char mm[16]; 也就是说存储部分都有计算出来的md5,经过思考之后觉得可以放弃存储md5,不存储md5是个很妙的想法,继续发挥hash思想,也不保存根据md5计算出来的hash值本身,只将该md5和串的信息关联到hash值的模所在的索引节点,这样就将索引节点信息减少到极致:

        size_t coffset;                  //content offset low

        unsigned short a:12;       //切分为12, 4

        unsigned short b:4;         //4,为下一个冲突值的索引序数,如果没有就为0

        size_t nextindex;             //冲突条目的存储序号,为0表示没有冲突

 

使用该索引可让单文件最多支持内容16T,最多687亿记录,具体实现的时候由于全使用文件所以速度比较慢,速度退化到sqlite之类同一级别了,不过这个设计思想为方案5提供了借鉴,如果跟方案5一样用大块内存辅助,速度大概可以上升一个级别,不过由于没有具体实现,待研究之后再做评估。

 

方案5hash紧缩内存方案

学习方案4的设计思想,考虑仅在内存里面实现一个紧凑型文件,由于只考虑内存可表示的32位范围,所以简化索引节点定义如下:

Size_t coffset;          pass相对于内容区首的偏移

Size_t nindex;          冲突节点下一个序,如果为0则表示没有冲突

内容区存储更简单,每个字符串直接保存,最后的0也保存,这样每个字符串自然分开,对一个6个字符长的串来说,保存一个信息只需要15个字节,真的是省啊,1亿个字符串也只要大约1.5g左右硬盘就够了。此方案虽然很妙,但实现的时候却费了一些周折,具体做的时候也做过好几个版本,由于考虑该方案的内容和索引最后都可以直接保存到文件,所以该方案对位置的保存都用的是相对位置,也由于想让索引节点信息简单,最初是让冲突索引采用线性步长跳跃方法,测试之后发现这个方法速度奇慢,而且还有个非常讨厌的问题,随着数据量的增多冲突扩散越来越厉害,耗时非线性的陡峭增长。放弃这个实现之后还是回到了经典的拉链法,拉链法速度就是快,但拉链法处理索引节点虽然容易,但要让索引信息可直接保存却要花一些脑子,最后采用先用内存扩展拉链,待全部索引构造好之后再把拉链出来的部分重新填到原始索引区中的空区,并修正对应索引相对位置。这个方法的精妙之处在于既省空间又有速度,最令人兴奋的是采用该方法耗时随着数据量的增大是线性增长,最后的实现在我的笔记本上大概100w/s1亿条记录从字母组合到最终生成索引文件也只要不到2分钟的时间,制造了一些数据之后统计了一下,冲突节点比例大概占26%-35%,也就是说有65%以上的数据只要一次hash就直接命中,平均拉链长度1.2左右,最长拉链10,总体还是很满意的。

 

原本第一版没有考虑这个可存储的方案,但花了几天就搞定了一个基本可用的存储方案还是很令人兴奋的,虽然该存储方案还有一些问题没有彻底解决,但已经有进一步处理的办法,待下一个相对空闲时间段再仔细研究一下,定会有更简洁的实现做出来,至于待解决的是什么问题以及如何解决那些问题还是等我代码写好了再写出来吧。

posted @ 2010-10-03 14:18 袁斌 阅读(178) | 评论 (0)编辑 收藏

HashCrack程序规划

 

最近几天在思考如何遍历md5找出原码字符串,但九十多个可见字符,96^6 782757789696,就算只遍历6位组合也是非常惊人的巨量数据,单机根本无法完成,根据常用密码合成规律,我们可以采用分而治之的方法解决,方法大致如下:

1、 使用很多机器,机器越多覆盖的范围越大。

2、 优先覆盖最常用的范围,如全数字组合,小写字母组合,小写数字混合组合,大写字母组合,夹杂一个非字母数字字符的组合,之后覆盖非常见组合,如所有可见字符的N个全排列。

3、 采用c/s结构,中心机管理区段分配策略,优先覆盖常用区段,所有slavemaster一起构成一个覆盖网。

4、 不仅支持md5,也支持sha等其他类似算法。

5、 Masterslave也支持添加字典,为了覆盖尽可能多的组合,可添加任何合适的字典组合。

 

实现难点:

1、 数据组织。

方案一、考虑过磁盘存储方案,研究了sqlite,写效率大概10w/s,加了索引大概5w/ssqlite存储效率也不是很高,比berkeley存储浪费很多空间,加了索引后空间放大了一倍。也尝试过berkeley方案,写效率大概只有4.5w/s,存储相对sqlite紧凑一些。但由于写效率太低,都难于实现我所希望的一个slave一下分配1亿数据量的模式(1亿数据大概要占磁盘5-10G,最经济模式大概也要占2.xG)。由于对btree+不太熟悉,暂时还没有找到高效的存储方案。

 

方案二、其实首先是考虑了内存方案,用内存相比磁盘其实要简单一些,不过受机器可用内存的限制以及客户接受程度的限制,难于让一台slave处理1亿以上的数据量,默认大概只能分配2000w-5000w左右的数据量(大概占用内存400M-1.2G)。

 

以上两种方案其实选择方案一更合适一点,因为一般的机器挤2G左右的磁盘基本都没有问题,甚至20G-200G可能都问题不大,但要持久占用1G内存可能不大好,毕竟大多数用户比容易接受。第一版暂时用了方案二,待高速写存储方案做好后替换一个dll即可(该dll已经留好了接口可直接替换)。

 

2、 区段划分

单纯的划分某一个区段也是比较容易的,如10个数字的8个组合,可用如下方法表示

Digits = “0123456789”

Range(Digits, 8, 0, 20000000) 表示digits 8个字母的排列 [0,20000000]

但将一些常用区段单独出来之后涉及到和更大范围字母全排列的重合情况就不大好扣出来,暂时没有找到较好的表示方法,好在小范围排列被冗余也问题不是很大,此表示方法待继续研究,暂时采用冗余方案。

 

3、 Master 发现机制

其实这是一个经典的问题,可考虑得很复杂,也可考虑得很简单,由于我名下几个域名都被禁止解析了,所以这个问题变得不是那么方便,不过暂时还是用个域名部署一下最为简单,存储空间也是个问题,待我联系几个朋友看看能不能找到一个合适的存储地点,待找到后第一版就这么部署出去吧。

 

 

实现方法:

由于要有一个中心提供调度方案,因此该系统肯定有一个中心(不管是一个点还是N个点),所以考虑采用c/s模式,第一版采用一个masterNslave的方案。

master主要实现以下功能:

1、 覆盖最常用区段排列,籍此提供最基本的查询服务。

2、 支持大量slave接入(暂采用iocp模式接入)。

3、 提供区段划分功能,slave接入断开自动分配区段,优先分配常用区段,其次分配非常用区段。

4、 查询转发功能,接收slave的查询请求,转发给其他slave

 

slave实现以下功能:

1、 master交互,接收分配的区段,处理区段内的数据并提供该区段数据查询服务(此功能由一个接口dll实现,可轻易替换)。

2、 支持查询功能。

3、 在客户端还计划支持apislaveapiproxyapi通过消息向slave发送查询请求,slave通过给master发送查询请求,在整个群落里面提供查询服务,此功能为该体系的一大亮点,暂时没考虑做什么限制,主要为吸引用户提供很主动的编程功能,第一版通过一个cdll提供api调用功能,在此api基础上用户应该可以很容易包装出其他语言的调用接口,如luapython的接口等。

 

 

进度说明:

由于该项目只是一个即兴性项目,没打算耗费很多时间,计划在一周内完成,已经过去了3天,所以在细节上暂时无法抽出很多时间仔细研究,待整体功能成型后看情况再斟酌算法,易改变部分暂时都用接口dll实现,该项目代码部分大概完成了一半左右,未来2-3天左右大概可以做完第一版。

posted @ 2010-10-03 14:17 袁斌 阅读(157) | 评论 (0)编辑 收藏

Jsonajax领域很流行,记得当时看过它的介绍后很兴奋,网上找了一些解析jsonc

 

c++代码,不过没有找到特别好的,有的写得不错不过要依赖于boost,有的用c写的不大好用,好在json语法简单,参考了一些c/c++json解析代码做了一个json类,最近又把去年写的json类修改为unicode下使用,增了一些功能,现在CJsonw可以解析const char *型输入,也可解析const wchar_t *型输入,可解析ansi编码、unicode编码、utf8编码的json文件。看看我的CJsonw定义:

 

        JsonwType type;                     //Json类型, true false null直接由类型表示了

        DWORD tag;                            //tag,用户自用值,默认为0

        union

        {

                struct{

                        wchar_t *cstring;//字符串型值

                        int clen;            //cstring按照字符计算的分配个数,包括可能的0

                };

                double dvalue;          //double

                struct

                {

                        int ivalue;         //int

                        DWORD dwhigh;       //高部

                };

                __int64 i64value;        //int64

                OBJS *objs;                     //对象型值

                ARRAYS *arrays;             //数组型值

        };

Sizeof(CJsonw) == 16

OBJS类型描述无序的key-value型数据,ARRAYS描述 array型有序数据,定义如下:

        typedef CHashiW<CJsonw *, CJsonw *> OBJS;

        typedef std::vector<CJsonw *> ARRAYS;

支持以下构造函数:

        CJsonw(JsonwType t=json_null);

        CJsonw(int value);

        CJsonw(__int64 value);

        CJsonw(float value);

        CJsonw(double value);

        CJsonw(const wchar_t *value);

        //数组型构造函数

        CJsonw(int *numbers, int count);

        CJsonw(__int64 *numbers, int count);

        CJsonw(float *numbers, int count);

        CJsonw(double *numbers, int count);

        CJsonw(const wchar_t **strings, int count);

支持以下赋值函数:

        bool setnull();

        bool set(bool value);

        bool set(int value);

        bool set(__int64 value);

        bool set(float value);

        bool set(double value);

        bool set(const wchar_t *value);

        bool set(int *numbers, int count);

        bool set(__int64 *numbers, int count);

        bool set(float *numbers, int count);

        bool set(double *numbers, int count);

        bool set(const wchar_t **strings, int count);

       

        //修改常规值,就是true, false, int, real, string

        bool setbystring(const wchar_t *value);

 

支持以下输入:

        bool parse(const char *string);

        bool parse(const wchar_t *string);

        bool parsefile(FILE *fp);

        bool parsefile(LPCTSTR filename);

文件可以ansi编码、unicode编码、或者utf8编码

 

支持以下wchar_t型输出:

        //dump,默认为unicode编码

        //uunicode表示字符串中的unicode字符是否按照\uxxxx格式输出

        //bDisptrue按照友好格式显示,会输出\t换行等方便阅读

        //bDispfalse按照紧凑模式显示,没有多余字符,方便网络传输等场合

        bool dump(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=false, bool bDisp=true);

        //跟上面的dump一样,但如果成功会在buf里面插入一个'\0'字符

        bool dump0(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=false, bool bDisp=true);

        bool save(LPCTSTR filename);

        bool save(FILE *fp);

保存到文件默认为UNICODE格式

 

支持以下ANSI型输出:

        //a系列dumpansi编码输出

        //bDisptrue按照友好格式显示,会输出\t换行等方便阅读

        //bDispfalse按照紧凑模式显示,没有多余字符,方便网络传输等场合

        bool dumpa(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=true, bool bDisp=true);

        //跟上面的dump一样,但如果成功会在buf里面插入一个'\0'字符

        bool dumpa0(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=true, bool bDisp=true);

ANSI型输出主要用在节省网络带宽或者和其他系统交换数据的情况下。

 

支持以下类型判别函数:

        //类型判别函数

        bool IsArray() const { return type==json_array; }

        bool IsObject() const { return type==json_object; }

        bool IsInt() const { return type==json_int; }

        bool IsReal() const { return type==json_real; }

        bool IsNumber() const { return IsInt()||IsReal(); }

        bool IsNull() const { return type==json_null; }

        bool IsTrue() const { return type==json_true; }

        bool IsFalse() const { return type==json_false; }

        bool IsString() const { return type==json_string; }

 

支持以下一些直接取值函数:

        const wchar_t *getordef(const wchar_t *strdef)

        int getordef(int idef)

        __int64 getordef(__int64 idef)

        double getordef(double ddef)

        bool getordef(bool bdef)

        //array的元素

        CJsonw *get(int n)

        CJsonw *operator[](int n) const

        //obj的元素

        CJsonw *get(const wchar_t *name)

        CJsonw *operator[](const wchar_t *name) const

 

支持以下取array型子元素数据的函数:

        const wchar_t *getordef(int n, const wchar_t *strdef)

        int getordef(int n, int idef)

        __int64 getordef(int n, __int64 idef)

        double getordef(int n, double ddef)

        bool getordef(int n, bool bdef)

 

支持以下取obj型子元素数据的函数:

        const wchar_t *getordef(const wchar_t *name, const wchar_t *strdef)

        int getordef(const wchar_t *name, int idef)

        __int64 getordef(const wchar_t *name, __int64 idef)

        double getordef(const wchar_t *name, double ddef)

        bool getordef(const wchar_t *name, bool bdef)

 

支持以下obj遍历函数:

        //遍历函数,最常见写法:

        //for(CJsonw::OBJIT *p=xxx.FirstObj(); p; p=p->next())...

        OBJIT *firstobj()

 

        //arrayobject元素个数,string类型返回分配的字符个数(包括可能的),其他类型都返回

        int size() const

 

另外支持一些增加array子元素函数:

        bool add(CJsonw *pnode)

        bool del(int n, bool bfree=true)

        bool addnull(){ return add(new CJsonw(json_null)); }

        bool addtrue(){ return add(new CJsonw(json_true)); }

        bool addfalse(){ return add(new CJsonw(json_false)); }

        bool add(int value){ return add(new CJsonw(value)); }

        bool add(__int64 value){ return add(new CJsonw(value)); }

        bool add(float value){ return add(new CJsonw(value)); }

        bool add(double value){ return add(new CJsonw(value)); }

        bool add(const wchar_t *value){ return add(new CJsonw(value)); }

 

也支持obj增加删除子对象函数:

        bool addobj(const wchar_t *name, CJsonw *pnode)

        bool delobj(const wchar_t *name, bool bfree=true)

        bool addobjnull(const wchar_t *name){ return addobj(name, new CJsonw(json_null)); }

        bool addobjtrue(const wchar_t *name){ return addobj(name, new CJsonw(json_true)); }

        bool addobjfalse(const wchar_t *name){ return addobj(name, new CJsonw(json_false)); }

        bool addobj(const wchar_t *name, int value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, __int64 value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, float value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, double value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, const wchar_t *value){ return addobj(name, new CJsonw(value)); }

 

还有一些特殊函数:

        //计算整个树的crc

        DWORD calccrc();

        //计算整个树的md5,要求md5不少于个字节

        byte *calcmd5(byte *md5);

        //计算hex表示的md5,不写尾部,要求md5hex不少于个字符

        char *calcmd5hex(char *md5hex, const byte x='x');

        //计算hex表示的md5,写尾部,要求md5hex不少于个字符

        char *calcmd5hex0(char *md5hex, const byte x='x');

 

 

json类上花了较多时间,主要是觉得这个类很有用,可以描述任意对象,易保存易传输易构造,对速度要求不苛刻的应用程序用json作为基本数据结构很合适,配合json里面的data自定义字段理论上支持描述任意对象,配合使用一些cache可扩大json类的使用范围,用它取代ini xml等配置简直是大材小用,用json类来做stringtable简直就是小菜一碟,而且还是hash级高效率,丝毫不比手工做一个hash_map<,>表差。

在实际应用中,去年有一个网络验证的项目就大量使用了这个类,配置、帮助都是json格式,应用中管理大量对象的内存数据库也是用了json类,编码后直接网上传输,server内部也是一个json对象直接管理了大量用户的信息,非常方便,可以说用json类使得这个程序少写了大量代码,也使得整个项目只用了1个月时间就做出来了,还部署成master – master- slave(N)模式稳定运行了将近1年时间。

在另一些网络应用中,俺将json格式数据作为变长包使用,前面俺写过一篇短文介绍了变长包最常见的几种格式,分别是:

1、 key\0value\0…

2、 json格式

3、 xml格式

使用json格式作为变长包现在很流行,这也是大量ajax所采用所依赖的技术,qq种菜,google搜索预列表都是采用json格式的。

posted @ 2010-10-03 14:17 袁斌 阅读(889) | 评论 (0)编辑 收藏

写了tmpool之后又发现居然有tcmalloc,而且比牛逼的ptmalloc好很多,真感叹啊,世界上好东西很多,但需要智慧的眼光去发现。在linux下简单比较了一下malloc和tcmalloc,快的真不是一点点啊,看到好多人用tcmalloc去重编译mysql, squid,nginx等。简单看了下tcmalloc的原理,比我写的tmpool要多一些整理的功能,大概tmpool就是tcmalloc在win下的子集,俺的tmpool区区800行代码自然是不能去跟tcmalloc比的,不过对付一般的server端程序还是够用的,经过一段时间的使用效果也还可以,待有时间对比下俺的tmpool和tcmalloc。

posted @ 2010-10-03 14:16 袁斌 阅读(1087) | 评论 (0)编辑 收藏

简单总结下asp.net状态维持方法如下:

client端状态
1、ViewState
2、ControlState
3、Hidden Field (post 模式)
4、Cookie
5、QueryString (get 模式)

server端状态
1、Application (全局)
2、Cache (全局)
3、Session (current session,配合后台驱动可将session持续化到很多地方,如xml,
或者db、或者memcached 等地方)
4、配置文件(asp.net有一个sqlprofileProvider类使您可将配置文件数据保存到sql数据库中)
5、db

对保持连接的c/s程序来说,client端状态我们不是特别关心,除了cookie也有一些参考意义,其他几种方法很难直接在普通cs程序里面对应,对server端程序开发来说,asp.net的server端数据持续方法对我们很有参考价值,对一般的游戏程序来说,db是必然会用的,配置文件型的也可参考使用,session型的很多程序都在使用,application型的缓存估计也有很多程序在用,更有价值的是cache型,我们一般的s程序基本都没有实现时效性cache和文件触发性cache,asp.net的cache对我们很有参考价值。当然我们不必生搬硬套的照搬web上的概念,只要学到精髓就可以了,总之,还是我坚持的那句话,侠义地说是从web架构学习server端程序设计,更广义地说是从web架构学习一般网络应用程序设计。

posted @ 2010-10-03 14:16 袁斌 阅读(155) | 评论 (0)编辑 收藏

我一直坚持一个观点,从web体系学习服务器端程序设计,web作为发展最早也是前景最广阔的internet模型,几乎支撑了网络世界一半以上的应用,其他email ftp等传统应用虽然产生时间也很早但几乎都很没落,只有web风采依旧,蓬勃发展,从apache到lighttp到nginx等,一个接一个高性能的web服务器程序分别被开发出来,上层应用cgi, isapi(nsapi), fastcgi助力,html协议也在不断发展,html5也要登堂亮相,各种脚本语言争相斗艳,python、php、java、asp.net等。web应用支持了目前最大规模的数据google,支持了最大量用户的访问,因此各种存储、云计算等都是首先在web领域获得应用,可以说web就是it领域的创新源泉,看下apache下面有多少项目就可见一般。web体系可谓博大精深,从web体系学习服务器端程序设计是学无止境的,从nginx等服务器程序设计上可学习server程序如何分块、如何高效,从isapi/nsapi模型上可学习如何用进程内模块扩展应用,从fastcgi可学习如何用进程外模块扩展应用,从各种cache方案中可学习如何为server端提速,从各种脚本应用上可学习如何为server端程序二次开发助力,web是源泉,web的各种这方式几乎概括了server端程序的各种架构模型。当然一般自定义协议服务器和web还是有些地方不同的,最显著的区别一般也就在于协议不一样,另外就是web是无状态一般不保持连接,因此web需要传递各种session等维持状态的数据,总之从web可学习的地方实在是太多。

简单类比下web领域开发和一般server的扩展

nginx模块/apache模块            server程序+plugin

cgi                                      fork子进程

fastcgi                                 server proxy模式,由于fastcgi可网络方式部署,所以很容易在一个局域网内部实现一个ioserver+N logicserver模型

简单类比就可知道,如果一个server程序只要能很方便的支持plugin+fastcgi就可适应几乎所有各种需求,可惜的是一般的server程序几乎都不支持这两个模式的扩展,大多数gameserver只支持lua等脚本,说实在的,支持个脚本跟支持fastcgi和plugin模块相比还是差很多,如果支持fastcgi或者plugin只要做个plugin就支持lua了。

posted @ 2010-10-03 14:15 袁斌 阅读(213) | 评论 (0)编辑 收藏

常见cs程序自定义数据包描述

 

常见cs程序自定义包可分为块型包、非块型包,非块型包如http协议的,用\r\n\r\n结束,我们这里重点讨论块型包,块型包常见头部如下:

Struct PKHEAD

{

        Union

{

                DWORD type;

                Struct

{

        WORD mtype;

        WORD stype;

};

        };

        DWORD len;

        Char buf[0];

};

内容buf部分常见组织方式莫过于定长、变长两种。定长的很简单,如struct item{…};作为内容部分,如果不涉及到高低序问题很好办,涉及到序的时候转换一下。变长部分常见组织方式有这么一些经典方法:

1、 Content1 \0 content2 \0 content3 \0 …

2、 json描述。

3、 xml描述。

方法1是效率很高的,对ansi型字符串或者utf8型字符串都没有问题,对utf16型字符串不行。解析这类变长部分的时候特别要注意包尾部的0,或者在缓冲区外部总保证存在一个0,或者保证包内尾部总存在一个结尾0,总之最后一个string要保证有一个合法的0结束,当然可能涉及到一些数值型到string型的转换可能会导致效率下降,1的缺点在于只有一个层次。如果将内容看作key1\0value1\0key2\0value2\0…可解决无序问题,但终归只有一个层次,不方便表示树形数据。

方法2的效率介于13之间,2由于描述很简洁,解析也是比较快的,比1好在可以解析任意层数据,嵌套N层都无所谓,阅读也很方便,解析的时候如果原始缓冲不释放可让字符串不复制直接记录在原始缓冲中的指针,这样可进一步提高解析效率。QQ种菜的通讯协议就是json格式的,阅读很方便,解析效率也可以,总的效率适中。

方法3的解析效率是最低的,好在xml描述性也比较强,解析代码也很多,跟上层应用交互的时候也比较方便,作为一个支持变长的选择也是可行的,不过如果特别在意效率的情况下避免使用,xml的解析效率比 2低了近一个数量级,比1可能低近2个数量级。

 

当然自定义包还有更多格式选择,标准协议也有很多如im 里面常见的xmppgoogleprotocol buffers等,还有json的一些扩展协议,如bjson等,如果只是处理一般的网游数据可能用不上这么复杂的协议,用上面提到的定长struct+1 or 2 or 3基本可以解决所有协议需求。也见到一些朋友自定义类型信息描述格式,个人觉得或许没有必要,特别是json如此流行的情况下再自己定义似乎有重复发明轮子的嫌疑,当然具体情况具体对待,一切以满足需求为目标。

posted @ 2010-10-03 14:15 袁斌 阅读(311) | 评论 (0)编辑 收藏

我写东西一般是即兴性创作,一般在走路的时候很容易产生各种想法,高兴的时候到家或到工作的地方马上就开写,几分钟就将想法记录下来,由于写得快也不检查,所以看上去可能不是很流畅,或者说得不是很清楚,甚至还有很多错别字等,请见谅。如关于某个技术问题想和我深入探讨请和我直接联系,qq:345585946

posted @ 2010-10-03 14:14 袁斌 阅读(118) | 评论 (0)编辑 收藏

Server模块如果仅仅作为一个lib包装出来自然不是最好,虽然可在c++工程很容易使用,效率也比较高,但要使用到其他语言上去就没有办法,如果包装一下自然是最好的,但如何包装呢,包装成一个什么样子也是很重要的,最常见的包装自然是包装成一个dll,输出一些接口,其他上层模块在该接口的基础上使用。最简单的包装一般可输出如下一些接口,

 

Public IServer:

{

Virtual void StartServer(LPCTSTR ports, LPCTSTR udpoorts, int iothreads, int syncthreads, int asyntchreads);

Virtual void StopServer();

         Virtual IClient *CreateClient();

         Virtual void CloseClient(IClient *);

         Virtual void OnCloseClient(IClient *);

         Virtual void OnConnectClient(IClient *);

         Virtual void PostSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam)

         Virtual void PostAsyncEvent(DWORD event, WPARAM wparam, LPARAM lParam);

         Virtual void OnAsyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void OnSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void OnTimer(UINT uid);

         Virtual void AddTimer(UINT uid, DWORD tk1, DWORD duetk);

};

 

Public IClient

{

         Virtual bool SendData(void *pdata, int len);

         Virtual bool SendFile(LPCTSTR lpfilename);

         Virtual bool SendFile(HANDLE hFile);

         Virtual bool SendMsg(PKHEAD *ph);

         Virtual bool SendMsg(PKHEAD *ph, void *pdata, int len);

         Virtual bool SendMsg(PKHEAD *ph, void *pdata, int len, byte encrypt, byte compress);

         Virtual void DelayClose();

         Virtual void OnDelayClose();

         Virtual void OnClose();

         Virtual void OnConnect();

         Virtual void OnAsyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void OnSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void PostSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void PostAsyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void SetTag(DWORD wParam);

         Virtual DWORD GetTag();

         Virtual long GetId();

};

 

IServer *DllCreateObject();

 

 

通过结合LoadLibrary并提供一个类似DllCreateObject的形式可很容易的 模拟类com实现,上层可很容易的是复用dll的实现。

posted @ 2010-10-03 14:14 袁斌 阅读(138) | 评论 (0)编辑 收藏

服务器程序可以使用以下脚本

1、 wow大规模的使用lua,已经导致lua成为网络游戏领域第一大脚本语言,速度和简洁性是它的突出特点,丰富的包装库也使得它的使用很简单,但库偏少和没有完整的面向对象特性也导致它不优雅,有的时候需要自己包装太多东西,总之还不是一个完美的东西。Tcc, python等也是服务器脚本的可选语言,tcc速度更快,python库丰富。

2、 我一直说server程序的开发可类比web领域的开发,web提供了丰富的脚本,如phpc#等,在一般的server程序里面集成php也是很容易的,php提供了丰富的库,如果要开发和web交互频繁的程序那么php的优势就更明显了,使用php也可以跟php作为普通web脚本一样有几个选择,如可当作进程内模块使用、也可以当作cgi使用,也可以当做fastcgi使用,不过作为普通服务器程序和一般的web程序还是有一些区别的,主要有两个区别,那就是web是无状态的,一般的server可能有状态也可能无状态,但大多都是有状态的,第二个不同就是web支持的是http协议,而一般的网游支持的是自定义协议,用phpserver端脚本除了这两个地方要注意之外就和在web上使用没多大区别了。包装好和主模块的交互就一切搞定了。

3、 2可知道,不仅仅是php可作为server程序的脚本,c#包括所有.Net平台支持的语言都可以作为server程序的脚本,c#随着4.0的发布已经成为.net平台上的首选语言,优雅的语法和高效的性能让它激动人心,比php lua等高级了许多,也高效了许多,说它是明星语言一点不过分,如果server可以在.net平台上,那么c#几乎是最优的脚本语言,同理vb.net c/clr等只要是.net平台支持的语言也都可以作为脚本,最典型的支持我们可以从sqlserver了解到,sqlserver支持clr型存储过程,这就是将.net作为脚本的典范啊。

4、 其实最简单的使用dll动态装入模型也可支持server端的灵活性,而且兼顾了效率,最简单的处理可以这样,每次调用dll部分的时候都LoadLibrary,用完之后FreeLibrary,虽然这样损失了效率,但灵活性比较好,要更新也很容易,当然如果管理到仅在dll更新的时候才重新装入可让效率更高,总之这种模型虽然没有什么人在使用,但其实不失为一种很有效的模型,开发效率和执行效率都比较高,虽然看上去不如用脚本那么优雅。

posted @ 2010-10-03 14:13 袁斌 阅读(362) | 评论 (0)编辑 收藏

仅列出标题
共4页: 1 2 3 4