战魂小筑

讨论群:309800774 知乎关注:http://zhihu.com/people/sunicdavy 开源项目:https://github.com/davyxu

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  257 随笔 :: 0 文章 :: 506 评论 :: 0 Trackbacks

#

主要修改:

更新了文档(这是必须的)

新的无符号整数C API

位处理函数重命名

更好及更快的支持双精度浮点数转换为整型

 

原文在此

 

可以看到,我在年初发文指出的lua数字处理方面的bug已经得到很好的解决,不过这种解决方法只是添加了api而已,看来还是没有松鼠脚本处理的彻底,原文在此

posted @ 2010-11-14 22:07 战魂小筑 阅读(2390) | 评论 (2)编辑 收藏

QQ替代方案:

Gtalk http://www.google.com/talk/     SSL加密聊天,免费永久漫游保存聊天记录

飞信:http://feixin.10086.cn/download/pcclient/  短信,PC沟通更好

 

QQMail替代方案

GMail https://mail.google.com/mail/?tab=ym  SSL加密,与Gtalk互动,容量不断增长

 

浏览器+浏览网页替代方案(当然,我不可能用QQ浏览器的)

Chrome+Google Reader,都支持手机版互动,订阅推送,免费翻墙阅读

posted @ 2010-11-04 10:34 战魂小筑 阅读(1213) | 评论 (1)编辑 收藏

Google Doc很长时间无法使用,火了,GFW又把地址屏蔽了

修改hosts文件:

209.85.225.101 docs.google.com

74.125.127.100 writely.google.com

74.125.127.139 spreadsheets.google.com

209.85.147.109 pop.gmail.com

209.85.147.109 smtp.gmail.com

66.102.7.19 mail.google.com

209.85.225.101 docs.google.com

209.85.225.102 groups.google.com

74.125.127.100 services.google.com

74.125.127.100 sites.google.com

209.85.225.104 reader.google.com

74.125.127.101 calendar.google.com

posted @ 2010-10-28 21:05 战魂小筑 阅读(1624) | 评论 (0)编辑 收藏

今天找到了贵论坛,发现坛主的很多想法和本人不谋而合,本人近1年主要精力都致力于开发一个大型多人在线游戏的基本架构和相关的技术模组。而我欣喜的发现我与坛主的研究方向正好相反:我是先从服务器端开始研究入手的,目前服务器端告一段落,正准备开始客户端的研发,在寻找客户端引擎的时候碰巧找到了这里。
我看到坛主的这个板块,了解到Orz正需要一些服务器方面的资料,在此我先奉上个人的服务器端的一些成果,希望能有所帮助。
(一)自己开发的一个基于boost::asio的网络引擎
首先这个网络引擎是基于boost::asio的一个封装,网络部分功能非常底层,API只有基本的listen、connect、send、kick等(均为异步,目前只实现了TCP协议),而其他方面提供的是基于mysql的db接口和log接口,还有一个自己开发的对象池,用于使用FreeList的概念来事先分配内存,降低运行时期内存的分配时间;
另外就是开发了一个多线程下的数据结构,一个线程安全的map,这个map可以让无限个线程同时读和写(包括添加元素、删除元素、修改元素)而无需任何因为互斥锁定带来的线程等待等开销。即是说1000个线程和1个线程操作这个map的效率是相同的。
发布形式:win32(64位未测试,但是开发考虑了相关的定制,例如指针和long在64位下从4字节提高到8字节,引擎底层做了数据类型的typedef)下 dll+lib+include;linux(Redhat、CentOS5.x,gcc3.4以上,需要安装boost1.37和mysql5.0)so+include;source code,yes,of course!
网络部分的基本结构是这样的:
#1 io部分设计。一个线程池负责处理io,这个实际上就是一组boost::asio::io_service::run,每个boost::asio::io_service下有一组私有线程,负责处理异步io事件,这里,boost::asio::io_service得数量和其下私有线程的数量是可以根据配置文件自由设置的,如果你了解boost::asio,那么一定知道它推荐一个cpu对应一个boost::asio::io_service对象(或者一个boost::asio::io_service,但是每个boost::asio::io_service下的私有线程对应每个cpu),这样在多处理器(或者多核处理器)下效率可以达到最高;
#2 complete handler设计。另一个线程池负责处理封装好的complete handler,即对应io事件的用户定义的逻辑处理,例如io recv事件,对应一个用户实现邦定的(使用boost::bind和boost::function)handler来处理当接受到socket消息的时候调用对应的handler(函数、仿函数对象、成员函数等)。#1和#2中的线程池之间使用一组线程安全的队列来传递消息(传递使用直接的值拷贝,不使用动态内存,因为动态内存的申请和释放太消耗时间,即便使用内存池也一样。1k以下的值拷贝的时间损耗都远远小于对应的动态内存申请的时间;另外使用值拷贝也有线程安全的考虑);
#3 封包的设计。head+body,head中有固定4字节的body长度信息(int32)和4字节的封包类型信息(int32),如果愿意,可以修改代码进行扩展(packet部分是独立于引擎的模块,也是一组dll,lib,include或者so,include),接受和发送由于是tcp,所以按照head中的body长度来控制一个封包的完整性。
#4 多线程模型。boss-worker模型,主要用于广播消息、查找、和db、log的实现上;生产者、消费者模型,主要用于#1和#2 的两个线程池之间的事件传递(io线程池产生completehandler,用户的线程池负责处理、消费)
#5 session的设计。一个session就是一个成功连接进来的客户端socket代理,出于线程安全和效率的考虑,session的存储容器不使用任何stl和boost的容器,而是使用——C的数组(当然不是静态数组,而是:new char[n]这样的),来实现。而且是二维数组,这样配合对象池(指与先分配好一组“空”的session),我们无需任何互斥变量就能够毫无阻碍的访问和修改数组中的每个元素(session),并发性能达到最大。至于二维数组的设计,第二维的值对应io线程池和用户线程池中的线程数量,即一个线程唯一邦定一组session,这是为了分配session id时候效率和安全的考虑。(例如id 0~9对应一组session,10~19是第二组,而每组id和session都唯一绑定一个线程)
#6 线程之间数据传递的设计。线程安全的queue,使用了boost::thread::locks中的mutex、shared_mutex、condition_variable_any等概念,当queue空闲时,使用条件变量等待特定的事件(例如新的元素push进来,或者程序退出等);
#7 异步下session的唯一性。由于整体是异步的,所以不可避免的会出现当一个session的某个处理还未结束的时候,这个session已经消失了,甚至换了一个新的session(指id的分配),那么这个时候如果没有任何防范处理,之前的那个未完成的处理很可能会作用到这个新的session上,就不可避免的出现一些错误和未知情况,我们如何防范呢?使用valid_code,设计一个64位的无符号整型变量,给每个session按照事先给定的session总量(这个引擎使用预先分配内存方法,所以开始前必须手动指定session的最大数量——通过配置文件),分配一个唯一的数据段(例如1~10000000,10000001~2000000等),每次这个session发现有新连接进来的socket使用了自己,则将自己的valid_code +1,当加到最大值的时候(例如刚才举例的10000000等),自动变为最小值(例如刚才的1等)。每个session的valid_code在引擎的初始化阶段是随机生成的(在事先指定好的数据段中随机)。再加上每个session的数据段时唯一的,所以不会产生重复的valid_code,这样鉴别某个时间段内唯一的session就成了可能;
#8 agent基类的设计。这个其实就是封装了boost::thread类,即“带私有线程的类”,主要用于作为一些相对独立的工作,例如log记录、db访问处理、定期ping某个ip等,引擎中的logger和db接口都是继承自它;
#9 db接口设计。一个数据库对应一个database对象,每个database对象拥有一个自己私有的线程池,其中线程的数量可以根据配置文件自由设定(例如和mysql的连接数量等,处理query的线程数量等)
#10 一些零碎。BIG_ENDIAN问题,封包内部自动进行了处理,无须用户单独设置;跨平台以及不同编译器的预编译设置,以及不同cpu的针对处理(x86,powerpc等)
目前的不足之处:
#1 并未开发完全。udp没有实现封装,当然boost::asio完全支持。logger目前只支持printf功能,对于写file和传递到专门的log服务器方面只留下了接口;封包加密以及安全方面是一个空白,目前的版本并未添加。
#2 面向底层,并不提供高层功能,所以很多开发都需要自己进行;(对,这并不是一个“网游引擎”)
#3 性能方面并未经过大量测试,由于本人工作较忙,这些都是业余时间搞得,所以只是初步测了一下连接并发部分:使用某个不知道配置的笔记本测得3秒并发连接1500。再多的并发连接并没有尝试过。
PS 由于本人工作较忙,故只能提供源代码(只提供windows版,便于查看,linux可以直接使用源代码编译,gcc3.4以上boost1.37即可),文档方面一直没有时间整理,这篇文章都是中午抽空写的(零零散散修改到现在),所以暂时就写这么多把。
PS2 提供的源代码是vs2005sln,只包含source code、配置和工程文件。
PS3 我看到贵论坛在研究RakNet,据我的一个前同事说,他认为RakNet并不好,网络底层用的是select,而且不是异步,代码质量不高,建议我不要使用它的网络层。我感觉RakNet的一些高层功能还是可以参考的,例如安全加密、大厅分流等,至于网络底层,我建议还是用boost::asio,跨平台、性能和扩展性都很优秀,而且被C++标准委员会所支持,很被看好。
作者:Nouness

posted @ 2010-10-27 17:03 战魂小筑 阅读(5528) | 评论 (2)编辑 收藏

我习惯把移动硬盘的盘符设为比较靠后的字母,这样每次看盘符就知道是哪个硬盘在用

比如,500G 为V盘, 80G 为W盘

 

今天突然想到,把2G的U盘,设为U盘符,嘿嘿,挺方便的

“靠到U盘,就是那个U:盘,不是硬盘”

posted @ 2010-10-22 18:05 战魂小筑 阅读(2116) | 评论 (0)编辑 收藏

测试环境:Visual Studio 2008 SP1

测试对象:RTTI的dynamic_cast和自己实现的RTTI系统,代码如下

        template<typename TClass>
        TClass* Cast( )
        {
            return IsKindOf( TClass::StaticGetClassInfo() ) ? (TClass*)this:null;
        }

 

    bool RTTIObject::IsKindOf( RTTIClass* ClassInfo )
    {
        RTTIClass* ThisClass = GetRTTIClass();
 
        if ( ThisClass == null )
            return false;
        
        return ThisClass->IsKindOf( ClassInfo );
    }

 

    bool RTTIClass::IsKindOf( RTTIClass* ClassInfo )
    {
        RTTIClass* ThisClass = this;
        while ( ThisClass != null )
        {
            if ( ClassInfo == ThisClass )
                return true;
 
            ThisClass = ThisClass->mParentClass;
        }
 
        return false;
    }

 

测试代码:

class ClassA : public RTTIObject
{
public:
DECLARE_RTTI_CLASS( ClassA )
int a;
private:
};
IMPLEMENT_RTTIROOT( ClassA )
 
class ClassB: public ClassA
{
    DECLARE_RTTI_CLASS( ClassB )
public:
int b;
private:
};
IMPLEMENT_RTTI_CLASS( ClassB, ClassA )
 
class ClassC : public ClassB
{
    DECLARE_RTTI_CLASS( ClassC )
public:
int c;
private:
};
IMPLEMENT_RTTI_CLASS( ClassC, ClassB )
 
class ClassD: public ClassA
{
    DECLARE_RTTI_CLASS( ClassD )
public:
int d;
private:
};

    ClassC c;
    ClassD d;
    
    ClassA* fakeC = &c;
    ClassA* fakeD = &d;
 
    const int TestTimes = 10000;
 
    float t1 = TimeSource::GetAppTime();
 
    for ( int i = 0;i<TestTimes;i++)
    {
        ClassC* realC = dynamic_cast<ClassC*>(fakeC);
        ClassD* realD = dynamic_cast<ClassD*>(fakeD);
    }
 
    float t2 = TimeSource::GetAppTime() - t1;
 
    for ( int i = 0;i<TestTimes;i++)
    {
        ClassC* realC = fakeC->Cast<ClassC>( );
        ClassD* realD = fakeD->Cast<ClassD>( );
    }
 
    float t3 = TimeSource::GetAppTime() - t2;
 
    SimpleLog log;
    log.Debug(L"%f  %f", t2, t3);

 

10000次,单位:毫秒   dynamic_cast     Cast
        Debug 1.468590 5.173067
        Release 1.025950 0.702404

 

可以看得出来,没有优化过的Cast代码性能极差,但是优化过的Cast性能超越了系统的dynamic_cast,跟踪汇编发现系统有做个一些异常及bad_cast的处理

建议:可以做一个宏,在不支持RTTI的编译器及平台下使用自己的Cast

posted @ 2010-10-22 16:00 战魂小筑 阅读(1298) | 评论 (1)编辑 收藏

Asio的架构:Boost.Asio 设计索引

概念性了解API:boost::asio中的同步与异步

Asio的Buffer: buffer几种用法,这些Buffer都只是引用外部的内存数据,如果需要拷贝和分配,记得使用boost::pool,这里还有一篇处理拷贝Buffer的文章

例子解析: Boost.asio的简单使用(timer,thread,io_service类)

如果照着例子弄出的第一个服务器无法收到客户端消息,试试这个asio::async_read与socket的async_read_some的区别

这里是另外一个区别:boost.asio库学习笔记—— receive和read的区别

 

从服务器连接过来的客户端的地址:

std::string endpoint = socket.remote_endpoint( ).address( ).to_string();

以下是对这篇文章的翻译:

asio chat_client.cpp中的一些问题

1. 有多少个线程在运行2个,还是3个?

>一般来说,依赖于运行的平台,从程序的角度来说是2个,包括:

*主线程,用于处理用户的输入输出

*io_service.run()线程,用于处理chat_client对象中的所有行为(action)

还有,async_write会创建一个线程或者其他的一些东西么?

>不会.

2. 有关1的问题,为什么write函数使用post直接调用?什么不调用async_write?既然调用了post,你只是将其放到一个队列里在同一线程处理,为什么之后还要从其他线程调用async_write?

chat_client的成员对象不是线程安全的(故意?),因此要同步处理这些成员。如果直接从主线程调用async_write不是线程安全的,因为此时可能有后台线程正在访问socket。

在这个例子中,所有的类成员都调用io_services.post()以保证在一个线程里访问,达到线程安全。io_services保证任何使用io_services.post()(或io_servies.dispatch())传入的句柄只会在io_serive.run()线程被调用。而且这个例子中只有一个线程调用io_service.run(),所以chat_client的成员变量也只会在一个线程中被访问。

4. 如果我想发送一个连接事件到主线程,怎样做?用io_service::post?能从主线程获取io_services?

在这个例子里是很困难的,因为主线程正在阻塞等待用户信息。不过如果你想将事件在线程间传递,确实可以为每个线程配备一个io_services。

5. 为什么在main函数的最后调用了t.join(),能用io_service.run()代替么?

不行,请参考问题2的解答,那样的话,线程安全将无法保证

6. 按照问题1的解答,如果有3个线程在运行(也就是,async_write被放到另外一个线程),那么哪个位置创建这个线程比较好?

因为主线程需要阻塞等待用户信息,因此io_service::run是唯一需要的。如果你的程序不需要这样做,那么就不需要其他线程,也就只需要简单的调用io_service::run()就可以了,这也是大多数例子这样做的原因

 

有关线程安全的问题

1. 对于asio对象,能从2个不同线程调用一个共享对象的不同成员么?

不能

那么其意义就是从2个不同线程访问共享对象不是线程安全的?

是的

只有被标记为 “共享对象:安全”的对象才能从不同线程同时访问,io_service就是这样的对象

2. 同样是线程安全的问题,对于basic_deadline_timer::cancel()我需要用io_service.post(boost::bind(&deadline_timer::cancel, &myTimer))方法封装调用么?

是的,直接调用cancel()也不是线程安全的

最好的解决方法就是使用io_service::post()将所有的操作都放在一个线程

3. asio有很多成员函数,我怎么知道哪些能安全的调用?

一般情况下,你应该认为没有任何一个函数是安全的,以下是通用的io线程安全判断用例:

write+write:不安全

read+write:不安全

read+read:安全

asio对象已经符合这种需求

 

这里有一篇介绍io_service众多区别及包处理,拆包等的技术

posted @ 2010-09-28 15:22 战魂小筑 阅读(2836) | 评论 (0)编辑 收藏

下载Boost1.44

解压后运行bootstrap,编译bjam

在命令行boost目录中里输入

bjam –with-system --with-regex --with-date_time --with-thread --with-seri
alization --toolset=msvc-9.0

这里使用的vc2008,其他版本请自行调整

 

之前试过网上其他教程,有这么写的

bjam --without-python --toolset=msvc-9.0 --with-thread --with-date_time --with-regex -
-with-serialization

 

结果报错error: both --with-<library> and --without-<library> specified

所以将without去掉,只写with就可以正常编译

 

使用时,包含boost_1_44_0\目录,lib在boost_1_44_0\stage\lib即可

posted @ 2010-09-26 13:12 战魂小筑 阅读(3173) | 评论 (0)编辑 收藏

D3D9下的获得RenderTarget有2种方法

1. 使用D3DXCreateTexture或者Device->CreateTexture 创建纹理

    调用Device->GetSurfaceLevel(0, &SurfacePtr );获得Surface指针

   将Surface指针使用Device->SetRenderTarget设置上去即可开始绘制

   注意:D3DXCreateTexture创建的是2的n次幂的纹理,而Device->CreateTexture 创建的则可以是任意大小的纹理

   这种方法创建的Texture与Surface是一一对应的,由D3D底层自动做了Resolve的过程

   不能使用MultiSample

 

2. 使用Device->CreateRenderTarget()创建一个Surface,用Surface直接设置为RenderTarget

  可以开启Lockable选项,但是效率会非常低

  可以使用MultiSample

   由于没有Texture的关联,这种方法绘制速度理论上会快一些

可以使用Device->StretchRect来将Surface直接拷贝到后备缓冲或者另外一个Surface。不过在DX8和某些DX9的驱动上有一定兼容性问题,具体请参考SDK

 

 

参考:

Render to Surface 

   http://www.borgsoft.de/renderToSurface.html

渲染到纹理(Render To Texture, RTT)详解

   http://www.opengpu.org/bbs/viewthread.php?tid=445

posted @ 2010-09-14 15:12 战魂小筑 阅读(4511) | 评论 (0)编辑 收藏

记得2008年时看过360开发机的XDK中的D3D中比PC版的多一个ResolveRenderTargetXX之类的函数,由于一直没有用到,没有理会。最近在整合引擎的的RenderTarget中,无意间搜到Console平台的Resolve的概念:

绘制完RT后,需要调用Resolve,将RT上的绘制结果复制到你的纹理之上。这个步骤是游戏机特有的,360的RT是只写的,PC是不需要这个步骤

posted @ 2010-09-14 14:36 战魂小筑 阅读(1495) | 评论 (0)编辑 收藏

仅列出标题
共26页: First 10 11 12 13 14 15 16 17 18 Last