饭中淹的避难所~~~~~

偶尔来避难的地方~

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  94 随笔 :: 0 文章 :: 257 评论 :: 0 Trackbacks
共3页: 1 2 3 
@非风

玩家登入登出,userserver不需要和数据中心交换数据.

数据中心只是查询和收集服务器信息或者个人信息的时候才需要和userserver进行交互。

玩家的角色信息都是保存在GroupDB里面的。
@非风
是的,差不多的.
不过我不会让AreaDB去数据中心请求数据。
我在上面说的是,只有用户进行激活,才会把用户资料放到AreaDB

而且点卡都是按照Area来冲值的。

所有的游戏需要的资料都在Area本地提供,不会向数据中心请求数据。
因为各个服务器的逻辑都是分开的,所以可以有选择的灵活部署,这样可以根据实际情况有效的节约成本.
1- PublicServer是组内跨服组队和聊天,也就是在一个服务器名字下面的跨服.这里跨服是物理服.

2- 控制和数据中心的功能是提供数据推送和信息收集,以及服务器状态控制.
他和userserver进行通信,是为了进行数据更新,信息收集和状态控制,在2的说明里已经提到了。

3- 这里面的所有db都不是单纯的一个数据库,而是一套称为dbproxy或者dbagent的东西,它有逻辑处理能力。
但是,它不会向数据中心要数据。在我的设计里,数据中心是请求者,而非被请求者。所有数据更新的发起者都是数据中心,因为所有用户信息的修改都在这里进行。AreaDB只是被动的接受数据中心的数据更新,而不向数据中心要数据。
re: 把图片转成ASCII码 饭中淹 2008-07-01 14:55
@foxtail
我想的方法是先对字模进行分级灰度计算,然后依次进行分级匹配,从而获得更精确的匹配。
re: 把图片转成ASCII码 饭中淹 2008-06-30 14:03
分级的灰度匹配么?
@Kevin Lynx
这样会造成很多误解啊。。。。
WAIT完成,再去LOCK一下MUTEX么?
@Kevin Lynx
是啊,当元素为0,又正好BLOCK=TRUE的时候,就会在POP里面死循环了。
因为POP里的GUARD锁住了MUTEX,PUSH的GUARD就会等在GUARD的MUTEX那里。这样的话,POP就永远等不到CONDITION的SIGNAL。
撇脚的退出方法,其实没什么,主要是个人喜恶。
我喜欢用标记,可以在任何平台下使用都没问题。


另外,我不明白为什么用了condition还要用GUARD
那样不是死锁了么?
BLOCK等待condition的时候,container被GUARD锁死在一个线程内,其他线程调用PUSH的时候都会被挡在GUARD那里。


re: Epoll笔记! 饭中淹 2008-06-24 08:39
额外的一个写数据队列或者缓冲是很重要的。
特别是需要处理很多socket的时候。

而且
EPOLL可以和线程池结合起来使用。用一个队列和一个线程池来模拟
IOCP,效率可能会比较高。
re: IOCP与线程 饭中淹 2008-06-23 23:45
@Kevin Lynx
写iocp应用的时候,我觉得最好能够实现工作线程就地组包,然后在组包之后,在工作线程内就地处理,这样是对iocp和cpu资源的最佳化利用。

还有,多谢你的帮助,我才纠正了对iocp的错误理解。

re: 完成端口(IOCP)编程探讨 饭中淹 2008-06-23 23:41
多个recv可以在overlapped上进行顺序绑定
recv请求的顺序,决定了它收到的数据的先后顺序。
只要进行简单的排序,就可以保证recv的数据的顺序正确。

如果用多个recv的话,可以直接去掉recvbuf了。对于一个socket,去掉recvbuf相当于是减少了内存复制,因为Io层会把WSARecv进去的buf拿来直接作为缓冲来保存收到的数据。

如果不去掉recvbuf,那么多recv就看起来不是那么有意义了。



另外,iocp的程序可以写的比较Open一点,不要老想着节省内存。对于buffer和overlapped结构,可以分别用一个池来管理,而不是固定写死。这样不需要等待处理完一次受到的buffer,就能紧接着发出下一次recv,而且在这个过程中overlapped这个结构还能够重用,效率自然就会提升的。




方法很妙
不过查找地址这个方法,我觉得可以稍微替换下。

以前,我做远程注入的时候,都是把API的地址做成一个结构体,通过WriteProcessMemory写到远程进程里。
在远程线程直接使用,或者对于不是kernel32的api进行初始化。

@Hellfire
还有星际的貌似也是这个方法.

另外,早期的字符表格也是用这种方法来进行自动拼接的。


自己写个解析struct的类,然后用这个来解析xml
re: Google App Engine! 饭中淹 2008-06-03 16:55
地址?
不错~
不过, 那个flash上,炮塔占4格,怪物按1格走。
支持先,我也在用c#做造塔游戏~~
能插入gif么?
这不是多线程的问题

当你先继承window后继承Listener的时候,App的内存结构如下:

class App
vt of Window
data of Window
vt of Listener
data of Listener
data of App

_beginthreadex的参数是void*,你把this传递进去,相当于传递CApp* this。其实隐含的就是Window*this,那么里面调用Listener->Show,自然就会去Window的vt里面查找对应索引的函数,就会调用错函数。

而第二个,因为你显式的=this,所以,编译器会进行转换,从而把正确的Listener地址赋值给那个全局指针,这时,无论继承顺序如何,都是正确的结果。

这其实是因为对象指针转换不准确导致的,不是vc的bug,也不是多线程的问题。
@alittlewolf
(1) 每组承载量,一般是10000人左右。在目前主流服务器硬件条件下,PublicServer为这些人做服务是轻松的。
(2) UserServer 没有太大压力,只是做为一个验证平台,角色管理的资源消耗也不大,这个在实际中已经验证过了。
(3) LoginServer 没什么必要分布式,虽然一般登陆的压力比较大,但是对于一个组来说,登陆验证都是消耗最小的一个操作,没有长期压力。而且它的硬件条件也是比较好的,所以不用分布式的,那样会增加整个系统的复杂性。
(4) 一般这种架构下,GroupDB和UserServer之间会做一个Cache,杜绝频繁的数据库读写。处于安全性和性能考虑,可以选择带有集群功能的数据库。不过从成本出发,一般是单台的DB服务器,一个库搞定。

(5) Agent是用来进行用户过滤,分流和调度的。同时也是出于安全性考虑。另外也是为了降低后台服务器的压力。而且用Agent可以实现很多比较效率的处理方式。
架构有严重问题......
re: 搭建通用构造器 饭中淹 2008-04-01 10:14
@mm
用map和vector根这个方法无关。用vector比较好理解这个查找过程。
re: 搭建通用构造器 饭中淹 2008-04-01 09:53
@raof01
我为了实现无差别的遍历, 所以让他继承.
这个可以用虚拟阅读者来理解.

虚拟阅读者需要知道函数的调用规则是什么样的,但是不需要知道函数的内部是如何实现的。所以,只要在虚拟阅读者用到一个东西之前去声明它,就可以了。不需要在虚拟阅读者之前去定义它。

声明,就是形容这个东西的样子。
定义,就是这个东西内部的原理和构造。
这个,分析到最后,怎么就分析到WINSOCK上去了呢。。。。抽象的分析是不应该扯上os和api的。
re: 不怕无知,但怕无畏 饭中淹 2008-03-21 09:16
最好的memcpy的实现应该是
void my_memcpy( void * _dst, void * _src, t_size _size )
{
memcpy(_dst, _src, _size);
}

另外,c++类的默认成员函数也要看用途来决定需要写哪些。

稳定排序,不稳定排序这是个概念问题,作程序的没必要去硬扣这个概念。

re: IOCP Tips 饭中淹 2008-03-13 12:33
NumberOfConcurrentThreads是指定系统同时调度的工作线程数.
如果你的工作线程的工作量很大,一定要把数量提升到比这个大.
这里的数量,最好和cpu的个数保持一个关系,这样可以达到最高性能的调度。
比如等于cpu数量这样一个选择。

IOCP的工作线程的概念就是提供给系统一个可调度的完成操作所需的线程,这样系统在获取队列的那个等待函数里面,就可以在内核中对所有调用这个函数的工作线程进行调度,在这些工作线程进入内核的部分处理完成队列中的未完成操作,从而实现高性能的io。

明白了这个原理,就可以根据这个原理来合理的配置每个iocp需要的参数了。当然,一个要注意的是,在获取完成状态的线程一定要大于等于你设置的这个同时调度的工作线程的个数的值,才能获得最高的性能。
这是啥工具?
re: 帮忙解释~关于内存问题! 饭中淹 2007-06-20 20:01
这个变量存放在堆栈内,堆栈是有一定的有效长度的
平台迁移会慢慢完善的
linux版也在开发中
以后会支持 linux的g++, windows下的 vc6 vc.net 2003 2005 devcpp 等流行的编译环境编译器
re: 有关 C++ 嵌套类 饭中淹 2007-05-23 06:50
试试便知。

我以前看过书上说类中类算是友元类,不应该能访问私有,可以访问保护。
re: 命令行界面VS图形界面 饭中淹 2007-04-26 21:42
其实.....命令行的作用是高可扩展性.
图形化的扩展性不是那么强.
@eXile
我看过CODEPROJECT上的DELEGATE的讨论.
我觉得实现一下才能亲身体会到里面的东西.
光用现成的东西, 感觉学不到东西了.

今天终于又知道, 类成员函数里面这么复杂的东西, 以前还奇怪多继承的THIS OFFSET的问题, 原来是这么的复杂...

看来这个方法是不能使用了.不过物有所值~~~
@eXile
又学到很多东西.......
这里面陷阱也很多啊~~做了这么多年都不知道....惭愧~~
@eXile
成员函数指针在32位平台会超过32位么?
@梦在天涯
@ChenA
这个方法的很大的缺点就是无法进行类型检测
带类型检测的我正在研究如何实现...
原来的实现这个功能的代码是以前用X86汇编写的,
刚修改成这种形式.
而且我刚学习模版的高级应用,请两位多指教.
re: 以前写的一个网络流封包类 饭中淹 2007-04-24 01:07
稍微...有个BUG...

析出的时候没有判断是否超出边界...
@wangdan
这,,你给的地址无效...
另外,你算是我前辈了, 我在盛大呆过1年.
这不是技巧.......
下面是我应用的地方....

1- 带参数的placement new封装
#define DEFINE_CALL_CON( paramcount ) template <class T, DP_STMP_##paramcount( typename, tp ) >\
inline T * CALL_CON( T * ptMem, DP_MTMP_##paramcount( tp, p ) ){\
T * pt = new(ptMem)T( LP_SNMP_##paramcount( p ) );\
return pt;\
}

DEFINE_CALL_CON(1);
DEFINE_CALL_CON(2);
DEFINE_CALL_CON(3);
DEFINE_CALL_CON(4);
DEFINE_CALL_CON(5);
DEFINE_CALL_CON(6);
DEFINE_CALL_CON(7);
DEFINE_CALL_CON(8);
DEFINE_CALL_CON(9);
DEFINE_CALL_CON(10);

template <class T>
inline T * CALL_CON( T * ptMem )
{
T * pt = new(ptMem)T;
return pt;
}

template <class T>
inline void CALL_DEC( T * pt )
{
pt->~T();
}

2- 内存池里创建带参数类对象用的

#define DEFINE_NEW_OBJECT(paramnumber) template <class T, DP_STMP_##paramnumber( typename, Tp )>\
T * POOL_NewObject( DP_MTMP_##paramnumber( Tp, p ) )\
{\
T * p = (T*)POOL_New( sizeof( T ) );\
if( p != NULL )\
CALL_CON( p, LP_SNMP_##paramnumber(p) );\
return p;\
}

DEFINE_NEW_OBJECT(1);
DEFINE_NEW_OBJECT(2);
DEFINE_NEW_OBJECT(3);
DEFINE_NEW_OBJECT(4);
DEFINE_NEW_OBJECT(5);
DEFINE_NEW_OBJECT(6);
DEFINE_NEW_OBJECT(7);
DEFINE_NEW_OBJECT(8);
DEFINE_NEW_OBJECT(9);
DEFINE_NEW_OBJECT(10);



template <class T>
T * POOL_NewObject( )
{
T * p = (T*)POOL_New( sizeof( T ) );
if( p != NULL )
CALL_CON( p );
return p;
}


3- 构造特性提取的缺省构造....
#define DEFINE_DEFCON_WITHPARAM( paramcount ) template <DP_STMP_##paramcount( typename, tp )>\
static inline T * con( LPVOID lpMem, DP_MTMP_##paramcount( tp, p ) ){return CALL_CON( (T*)lpMem, LP_SNMP_##paramcount( p ) );}


template < class T>
struct object_con_trait
{
static inline T * conCopy( LPVOID lpMem, const T & v )
{
return CALL_CON<T, const T&>( (T*)lpMem, v );
}
DEFINE_DEFCON_WITHPARAM( 1 );
DEFINE_DEFCON_WITHPARAM( 2 );
DEFINE_DEFCON_WITHPARAM( 3 );
DEFINE_DEFCON_WITHPARAM( 4 );
DEFINE_DEFCON_WITHPARAM( 5 );
DEFINE_DEFCON_WITHPARAM( 6 );
DEFINE_DEFCON_WITHPARAM( 7 );
DEFINE_DEFCON_WITHPARAM( 8 );
DEFINE_DEFCON_WITHPARAM( 9 );
DEFINE_DEFCON_WITHPARAM( 10 );

static inline T * con( LPVOID lpMem )
{
return CALL_CON( (T*)lpMem );
}

static inline T * conArray( LPVOID lpMem, size_t count )
{
//LPVOID lpNewMem = (LPVOID)(((int*)lpMem)-1);
//int orgMem = *(int*)lpNewMem;
//new (lpNewMem) T[count];
//*(int*)lpNewMem = orgMem;
T * pv = (T*)lpMem;
for( size_t i = 0;i < count;i ++ )
CALL_CON(pv+i);
return (T*)lpMem;
}
};
@梦在天涯
导出成员函数是可以的, 纯虚函数也是可以的, 但是似乎是没有什么作用.

一般我都是为导出类写纯虚接口类.
NATURAL就是编译器支持的原子类型
char, int, long, short, 这些以及他们的无符号版本版本

纯虚指针,就是纯虚函数指针,
比如在插件和主程序的参数传递中使用下面这种纯虚类的指针
class CPureVirtualParam
{
public:
virtual char * GetName() = 0;
virtual int GetAge() = 0;
};

UDP用IOCP也有优化效果.
不过不是那么明显.
如果有很多个UDP端口一起在监听和收发,效果会明显一点.
@ssss
我采用Complete序列号和Post序列号的方法.
每次发送一个RECV请求,Post序列号++.
每次完成一个RECV就判断一下Post序列号是否等于Complete序列号,等于,就处理掉, Complete序列号++,如果,不等于,则保存到临时数组,直到收到的RECV完成信息的Post序列号等于Complete序列号,处理掉,并查看数组里的保存的那些是否等于++后的Complete序列号,不断重复处理和Complete序列号++,直到完成信息的Post序列号不等于Complete序列号.

这样就能够保证顺序.
共3页: 1 2 3