獲得主機間延遲(ping time)
作為獲取 ProudNet 中主機之間的延遲(ping time or lag)的方法,
得到的ping time是roundtriplatency,即從這邊的主機向那邊的主機發送信息後,在那邊的主機立即應答所需的時間。
- 函數從伺服器取得一個客戶端的延遲
- 從客戶端獲得服務器或連接到P2P的其他客戶端的延遲的函數
從1.7.40679-master版本開始,增加了一個新的函數來計算客戶端和伺服器之間的延遲。
先前的 GetLast~ 或 GetRecent~ 等功能在網路狀況不順暢的情況下,會出現 ping pong 延遲的問題。
如果您可以使用1.7.40679或更高版本,我們建議使用該函數來計算新新增的伺服器的延遲。
對於Reliable消息傳輸方式,採用雙向通信方式,收發時間比Unreliable慢,但保證收發順序和到達的確定性。
對於Unreliable消息的傳輸方式,採用單向通信方式,傳輸時間比Reliable快,但傳輸順序可能會出現偏差,無法保證到達的確定性。
不同語言的程式之間進行通信
兩個程序通過ProudNet進行通信,但有時想用不同的編程語言製作。 在這種情況下,PIDL編譯器可以生成兩種或兩種以上語言的proxy和stub,然後每個程序都拿需要的東西寫下來。
對於int、double、string等基本類型,ProudNet的C++以外語言的Rapping模塊已經提供。 但是,如果語言不同,這種基本類型的名字基本上會有所不同。 例如,C# 的字符串類是 System.String,在 C++ 中是 std::string , std::wstring, ATL::CString , Proud::String。
爲了解決這個問題,PIDL編譯器提供在用戶需要時,將生成的proxy、stub中的變量類型僅限於特定語言的功能。
下面是一個用法範例。
// 生成C#語言的proxy、stub時,將A型更名爲B型。
rename cs(TypeA, TypeB);
// 生成C++語言的proxy,stub時,將C型更名爲D型。
rename cpp(TypeC, TypeD);
global XXX 2000
{
Foo([in]TypeA a); // 用C#語言生成proxy、stub時成爲Foo(Type Ba),
Goo([in]TypeC c); // 用C++語言生成proxy,stub時變爲Goo(TypeDc)。
}
自定義每個主機的數據 (Host Tag)
接收RMI或事件回撥時,一起接收Proud.HostID以進行識別。 開發遊戲服務器時,如果收到Proud.HostID,就以該值爲依據搜索相應Host的對象,然後處理其對象,ProudNet樣本程序也是以這種方式製作的。
Host Tag是指除HostID外,用戶還可以爲本地主機和其他主機定義的識別數據。
利用這些可以進一步提高程序的性能。
OnClientJoin(CNetClientInfo* clientInfo)
{
// OnClientJoin函數運行過程中主機退出時,SetHostTag可能會失敗,
// 因此最好如下確認。
CNetClientInfo outInfo;
if(m_client->GetClientInfo(clientInfo->m_HostID,outInfo))
{
Host* r = new Host;
SetHostClientTag(clientInfo->m_HostID, r);
}
}
DEFRMI_XXX_YYY(MYCLASS)
{
void* tag = RmiContext.m_hostTag;
// 如果 SetHostTag 失敗, RmiContext.m_hostTag 可能是 NULL, 需要檢查 。
if(tag != NULL)
{
Host* obj = (Host*)tag; // 無查找成本
obj->Something();
}
}
不使用 Host Tag 的示例
DEFRMI_XXX_YYY(MYCLASS)
{
Host* obj = Lookup(HostID); // 產生的搜尋費用
if(obj != NULL)
obj->Something();
}
Host Tag被指定爲以下方法。
CLanClient 和 CLanServer 從 1.7 版本開始不支持 。
NetClient 和 NetServer 接手 CLanClient 和 CLanServer 的角色。
Host Tag通過Proud.RmiContext.m_hostTag和Proud.CNetPeerInfo.m_hostTag接收,不會發生網絡同步,不會傳播給對方。
設定Thread Pool
ProudNet的網絡客戶端模塊CNetClient和服務器模塊CNetServer使用兩種Thread Pool。
Networker thread pool: 僅用於ProudNet內部處理,負責插座I/O處理等。
User worker thread pool: 用於執行自定義例程 。 處理RMI或回撥活動等
為了獲得最佳效能,請明確建立一個線程池並將其設置在網路模組上,以確保共享特定的線程池,而不是每個擁有這些線程池的模組。
當前客戶端使用此功能時,需要include ProudNetServer.h,1.7.36365 後無需include ProudNetServer.h。
用戶生成的thread pool客體在網絡模塊全部被破壞之前,如果被破壞,就會發生throw exception。
以下是與整體趨勢相關的假代碼。
// 負責線程池開始/結束的回撥的客體。
Proud::CThreadPoolEventFunctional e;
// 即,爲線程池的開始/結束添加一個程序。(可選)
e.OnThreadBeginFunction = [](){...};
e.OnThreadEndFunction = [](){...};
// 創建具有 CPU 數量的線程的 thread pool 對象 。
Proud::CThreadPool* p = Proud::CThreadPool::Create(&e, GetNoofProcessors());
// 想要2個線程的話
Proud::CThreadPool* p = Proud::CThreadPool::Create(&e, 2);
// ----------用參數傳達登記該thread pool的對象。----------
// 服務器端的情況
Proud::CStartServerParameter param;
param.m_externalUserWorkerThreadPool = p;
// 客戶端的情況
Proud::CNetConnectionParam param;
// 請參閱在新窗口中打開 ProundNet 線程模型類型鏈接
param.m_userWorkerThreadModel = Proud::ThreadModel::ThreadModel_UseExternalThreadPool;
param.m_externalUserWorkerThreadPool = p;
// 想要2個線程的話
Nettention.Proud.ThreadPool p = new Nettention.Proud.ThreadPool(2);
// ----------用參數傳達登記該thread pool的對象。----------
// 服務器端的情況
Nettention.Proud.StartServerParameter param = new Nettention.Proud.StartServerParameter();
param.SetExternalUserWorkerThreadPool(p);
// 客戶端的情況
Nettention.Proud.NetConnectionParam param = new Nettention.Proud.NetConnectionParam();
// 請參閱在新窗口中打開 ProundNet 線程模型類型鏈接
param.userWorkerThreadModel = Nettention.Proud.ThreadModel.ThreadModel_UseExternalThreadPool;
param.SetExternalUserWorkerThreadPool(p);
線程池設置方法基本與上述相同,根據您想要的Case添加不同的設置值。
不僅是服務器,客戶端也可以設置多個線程模型。
- 共享多個網絡模塊相同的Thread Pool
Proud::CStartServerParameter param1;
Proud::CStartServerParameter param2;
Proud::CStartServerParameter param3;
//使用 Proud::CThreadPool::Create() 創建 thread Pool 對象
Proud::CThreadPool* p = Proud::CThreadPool::Create(...);
//共享 user worker thread pool
//Networker thread pool默認設置保持不變,不共享。
param1.m_externalUserWorkerThreadPool = p;
param2.m_externalUserWorkerThreadPool = p;
param3.m_externalUserWorkerThreadPool = p;
Nettention.Proud.StartServerParameter param1 = new Nettention.Proud.StartServerParameter();
Nettention.Proud.StartServerParameter param2 = new Nettention.Proud.StartServerParameter();
Nettention.Proud.StartServerParameter param3 = new Nettention.Proud.StartServerParameter();
Nettention.Proud.ThreadPool p = new Nettention.Proud.ThreadPool(2);
//共享 user worker thread pool
//Networker thread pool默認設置保持不變,不共享。
param1.SetExternalUserWorkerThreadPool(p);
param2.SetExternalUserWorkerThreadPool(p);
param3.SetExternalUserWorkerThreadPool(p);
- 使用不同數量的 Networker 和 User worker thread pool
Proud::CStartServerParameter param;
Proud::CThreadPool* p1 = Proud::CThreadPool::Create(..., 1);
Proud::CThreadPool* p2 = Proud::CThreadPool::Create(..., 4);
param.m_externalNetWorkerThreadPool = p1;
param.m_externalUserWorkerThreadPool = p2;
Nettention.Proud.StartServerParameter param = new Nettention.Proud.StartServerParameter();
Nettention.Proud.ThreadPool p1 = new Nettention.Proud.ThreadPool(1);
Nettention.Proud.ThreadPool p2 = new Nettention.Proud.ThreadPool(4);
param.SetExternalNetWorkerThreadPool(p1);
param.SetExternalUserWorkerThreadPool(p2);
- 整合 Networker 和 User worker的thread pool
Proud::CStartServerParameter param;
Proud::CThreadPool* p = Proud::CThreadPool::Create(..., 8);
param.m_externalNetWorkerThreadPool = p;
param.m_externalUserWorkerThreadPool = p;
Nettention.Proud.StartServerParameter param = new Nettention.Proud.StartServerParameter();
Nettention.Proud.ThreadPool p = new Nettention.Proud.ThreadPool(8);
param.SetExternalNetWorkerThreadPool(p);
param.SetExternalUserWorkerThreadPool(p);
- 純單線程模型
這是爲了將純粹以單線程運行的服務器升空到CPU數量的方法,需要創建完全沒有線程的線程池客體,並持續呼叫手動讓線程池呼吸的函數。
指定 Networker thread pool 時, Process 或 FrameMove 等不能唱得太慢 。 回電的方法要儘快完成必要的處理。
否則,內部可能會出現ping測量異常或服務器與客戶端之間的連接中斷等。
Proud::CStartServerParameter param;
Proud::CThreadPool* p = Proud::CThreadPool::Create(..., 0);
param.m_externalNetWorkerThreadPool = p;
param.m_externalUserWorkerThreadPool = p;
....
void main()
{
while(true)
{
// 最多等待10毫秒,處理thread pool中堆積的活動。
p->Process(10);
}
}
Nettention.Proud.StartServerParameter param = new Nettention.Proud.StartServerParameter();
Nettention.Proud.ThreadPool p = new Nettention.Proud.ThreadPool(0);
param.SetExternalNetWorkerThreadPool(p);
param.SetExternalUserWorkerThreadPool(p);
....
static void Main(string[] args)
{
while (true)
{
// 最多等待10毫秒,處理thread pool中堆積的活動。
p.Process(10);
}
}
配置 Thread Model - 客戶端
上面分離了user worker和networker,防止發生錯誤,但FrameMove仍有需要明確呼叫的部分。
這提供了可以在需要時運行累積的RMI的優點。
客戶端的代碼複雜,調用Process或FrameMove時,無需擔心邏輯扭曲或因失誤導致相應代碼遺漏等情況,設定方法是Thread Model設置。
ProudNet對網絡提供多種操作方法,但也可以設置爲不在意。
- ProudNet 線程模型類型
ThreadModel_SingleThreaded:
RMI 和消息回撥在用戶呼叫 FrameMove 時完成 。 (默認)
ThreadModel_MultiThreaded:
用戶無需呼叫FrameMove即可完成回撥。
ThreadModel_UseExternalThreadPool:
當指定thread pool時,必須將其設定為此。
以上值可在CNetConnectionParam的m_netWorkerThreadModel和m_userWorkerThreadModel中設置。 默認值爲ThreadModel_SingleThreaded。
Proud::CNetConnectionParam param;
//user worker 和 network 兩個都想使用自定義線程池時
param.m_userWorkerThreadModel = Proud::ThreadModel::ThreadModel_UseExternalThreadPool;
param.m_netWorkerThreadModel = Proud::ThreadModel::ThreadModel_UseExternalThreadPool;
//如果您不想簡單地呼叫 FrameMove
param.m_userWorkerThreadModel = Proud::ThreadModel::ThreadModel_MultiThreaded;
Nettention.Proud.NetConnectionParam param = new Nettention.Proud.NetConnectionParam();
//user worker 和 network 兩個都想使用自定義線程池時
param.userWorkerThreadModel = Nettention.Proud.ThreadModel.ThreadModel_UseExternalThreadPool;
param.netWorkerThreadModel = Nettention.Proud.ThreadModel.ThreadModel_UseExternalThreadPool;
//如果您不想簡單地呼叫 FrameMove
param.userWorkerThreadModel = Nettention.Proud.ThreadModel.ThreadModel_MultiThreaded;
非同步運行使用者例程
您可以使用 Proud.IRmiHost.RunAsync() 非同步運行函數或 lambda 表達式 A。
Proud.IRmiHost.RunAsync() 立即返回,A 在線程池中的線程之一上運行。
如果A爲指定的HostID的主機H運行,則H的callback函數和A不能同時運行。
Proud::CNetServer* s = ...;
Proud::HostID r1 = ...;
string a = ...;
s->RunAsync(r1, [a] { WriteSomething(a); });
發送量過多時警告功能
ProudNet的伺服器和網路具有當傳輸量超過線路速度時進行偵測和警告的功能。包含ErrorType_SendQueueIsHeavy 的 Proud.INetCoreEvent.OnWarning 將被回呼。
發送量過多時,建議改善不必要的信息或發送量自動調節功能 (Throttling)。