re: 一種經典的網絡游戲服務器架構 飯中淹 2008-08-15 18:17
@非風
玩家登入登出,userserver不需要和數據中心交換數據.
數據中心只是查詢和收集服務器信息或者個人信息的時候才需要和userserver進行交互。
玩家的角色信息都是保存在GroupDB里面的。
re: 一種經典的網絡游戲服務器架構 飯中淹 2008-08-15 16:51
@非風
是的,差不多的.
不過我不會讓AreaDB去數據中心請求數據。
我在上面說的是,只有用戶進行激活,才會把用戶資料放到AreaDB
而且點卡都是按照Area來沖值的。
所有的游戲需要的資料都在Area本地提供,不會向數據中心請求數據。
re: 一種經典的網絡游戲服務器架構 飯中淹 2008-08-15 11:32
因為各個服務器的邏輯都是分開的,所以可以有選擇的靈活部署,這樣可以根據實際情況有效的節約成本.
re: 一種經典的網絡游戲服務器架構 飯中淹 2008-08-13 11:54
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
分級的灰度匹配么?
re: 建立異步操作組件:隊列和線程 飯中淹 2008-06-26 12:39
@Kevin Lynx
這樣會造成很多誤解啊。。。。
WAIT完成,再去LOCK一下MUTEX么?
re: 建立異步操作組件:隊列和線程 飯中淹 2008-06-26 01:52
@Kevin Lynx
是啊,當元素為0,又正好BLOCK=TRUE的時候,就會在POP里面死循環了。
因為POP里的GUARD鎖住了MUTEX,PUSH的GUARD就會等在GUARD的MUTEX那里。這樣的話,POP就永遠等不到CONDITION的SIGNAL。
re: 建立異步操作組件:隊列和線程 飯中淹 2008-06-25 18:49
撇腳的退出方法,其實沒什么,主要是個人喜惡。
我喜歡用標記,可以在任何平臺下使用都沒問題。
另外,我不明白為什么用了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
不錯~
不過, 那個flash上,炮塔占4格,怪物按1格走。
這不是多線程的問題
當你先繼承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,也不是多線程的問題。
re: 一種經典的網絡游戲服務器架構 飯中淹 2008-04-16 09:59
@alittlewolf
(1) 每組承載量,一般是10000人左右。在目前主流服務器硬件條件下,PublicServer為這些人做服務是輕松的。
(2) UserServer 沒有太大壓力,只是做為一個驗證平臺,角色管理的資源消耗也不大,這個在實際中已經驗證過了。
(3) LoginServer 沒什么必要分布式,雖然一般登陸的壓力比較大,但是對于一個組來說,登陸驗證都是消耗最小的一個操作,沒有長期壓力。而且它的硬件條件也是比較好的,所以不用分布式的,那樣會增加整個系統的復雜性。
(4) 一般這種架構下,GroupDB和UserServer之間會做一個Cache,杜絕頻繁的數據庫讀寫。處于安全性和性能考慮,可以選擇帶有集群功能的數據庫。不過從成本出發,一般是單臺的DB服務器,一個庫搞定。
(5) Agent是用來進行用戶過濾,分流和調度的。同時也是出于安全性考慮。另外也是為了降低后臺服務器的壓力。而且用Agent可以實現很多比較效率的處理方式。
re: 本次服務端搬平臺的一些體會。 飯中淹 2008-04-09 14:51
架構有嚴重問題......
re: 搭建通用構造器 飯中淹 2008-04-01 10:14
@mm
用map和vector根這個方法無關。用vector比較好理解這個查找過程。
re: 搭建通用構造器 飯中淹 2008-04-01 09:53
@raof01
我為了實現無差別的遍歷, 所以讓他繼承.
re: 開始有點理解自定義函數位置任意性 飯中淹 2008-04-01 02:31
這個可以用虛擬閱讀者來理解.
虛擬閱讀者需要知道函數的調用規則是什么樣的,但是不需要知道函數的內部是如何實現的。所以,只要在虛擬閱讀者用到一個東西之前去聲明它,就可以了。不需要在虛擬閱讀者之前去定義它。
聲明,就是形容這個東西的樣子。
定義,就是這個東西內部的原理和構造。
這個,分析到最后,怎么就分析到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...
析出的時候沒有判斷是否超出邊界...
re: 寫了5年的代碼,終于要舍棄了. 飯中淹 2007-04-21 01:07
@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序列號.
這樣就能夠保證順序.