客戶端與服務器通信

客戶端連接到服務器後,首先用TCP進行通信。

在此期間,如果通過後臺成功與服務器進行UDP孔穿孔,則可以進行UDP通信,但在此之前,reliable、Unreliable 信息全部被TCP代替。 但是自從UDP打孔成功以後,Reliable 信息就被UDP代替了。

客戶端用各自的TCP端口、UDP端口進行通信,服務器用1個TCP listening port、1個以上的UDP端口進行通信,客戶端在保持與服務器TCP連接的同時,從服務器側的UDP端口中選擇一個。 即,服務器方均勻地共享給連接多個UDP port的所有客戶端。

例如,如果4萬個客戶端與打開2萬個UDP port的服務器進行通信,服務器方面的每個UDP port就有2個客戶端進行通信。

註冊和使用 Proxy & Stub 通信對象

我們將通信添加到服務器和客戶端。 爲了方便起見,在創建服務器和客戶端都使用的Common(公用)方案後,準備PIDL文件。 定義從服務器向客戶端發送通信的協議,用於準備好的PIDL文件。

Global S2C 3000
{
   Chat(Proud::StringA txt);
}

編譯上述PIDL文件時,生成ProxyStub客體。

如何使用 Proxy 對象

請先將Header包含在您要使用的地方。

// Server: 通過Server ▶ Client 
// Server爲了進行呼叫 
// 包含 Proxy 對象。
// header file 宣言
// 創建Common項目
// 因爲假設了Common 方案的 
// 包含從文件夾創建的文件 。
#include "../Common/S2C_proxy.h"
  
// 在 cpp 文件中聲明
#include "../Common/S2C_proxy.cpp"

創建Proxy並註冊爲服務器對象。

// 創建對象。
S2C::Proxy g_S2CProxy;
  
void main()
{
    // 在 Server 描述中創建的 
    // Server 對象。
    CNetServer* srv = 
         ProudNet::CreateServer();
    Svr->AttachProxy(&g_S2CProxy);
  
    // 以下省略
}

使用AttachProxy函數,以移交生成的Proxy客體的指針的方式進行註冊。 AttachProxy通過內部排列管理,可以註冊多種PIDL。 如果註冊了,可以使用Proxy客體的函數進行通信。

// HostID 和 RmiContext 
// 會自動添加。
// 想要發送到hostID的 
// 輸入 Client 的 HostID 值 。
g_S2CProxy.Chat(
         hostID, 
         RmiContext::ReliableSend, 
         “Send Message”);

Proxy 一樣,stub 也會在要使用的地方包含 Header。

Client:
 Server ▶ Client,在Client中 
 包含 Stub 對象以接收呼叫 。
// header file 宣言
// 創建Common項目 
// 因爲假定, 所以在Common文件夾中 
// 包含創建的文件 。
#include "../Common/S2C_stub.h"
  
// 在 cpp 文件中聲明
#include "../Common/S2C_stub.cpp"

對於Stub客體,因爲是接收協議的定義函數,所以必須生成並使用繼承的對象。 使用AttachStub函數註冊後,相應呼叫到來時會回電。 生成的Stub客體內會產生定義(Define),如果使用它,即使變更協議,也不需要另外修改cpp文件和h文件。

#define DECRMI_C2S_Chat bool Chat(
        Proud::HostID remote,
        Proud::RmiContext &rmiContext,
        const Proud::StringA txt)

Stub Class 中指定的 Define 語句中, DEFRMI_NameSpace_函數名稱在繼承對象的 Header 文件中聲明, DECRMI_NameSpace_函數名稱 (Class_Name) 在 cpp 中聲明。

class CS2CStub : public S2C::Stub
{
public:
  
// 即使變更Protocol,用戶 
// 無需修改class,在stub內 
// 已處理爲define文。
// 以" DEFRMI_NameSpace_ 函數名" 
// 如果有, 在header 中聲明 。
    DECRMI_S2C_Chat;
};
CS2CStub g_S2CStub;
  
// 'DEFRMI_Protocol分類名_protocol名(
//               繼承的類 name'
// 如果設置爲 , 則在 cpp 中聲明 。
DEFRMI_S2C_Chat(CS2CStub)
{
    printf( 
         "[Client] HostID:%d, text: %s”, 
         remote, 
         txt);
  
         // 必須回傳 true
    return true;
}

'True' Return意味着已經處理完畢。 如果Return'False',則判斷爲用戶未對協議進行處理,OnNoRmiProcessed Event將返現。 在返回DEFRMI_S2C_Chat的函數因子中,remote是調用RMI的相對HostID值。 使用此ID值調用Proxy,可以將通信發送給您想要的對方。

現在將生成的Stub客體註冊到Client客體。

CNetClient *client 
         = ProudNet:CreateClient();
client->AttachStub(&g_S2CStub);
// 以下省略

AttachStub函數也在內部進行排列管理,以移交指針的方式進行註冊。

開發後必須檢查流量。 通信量需要在各客戶端、(如果使用Super peer功能,在Super Peer)服務器等分別檢查通信量,消除不必要的通信量。 爲了確認流量,請參考以下方法。

  • 使用 NetLimiter 等工具 。

  • 有一種方法是,準確執行每臺電腦使用的程序個數後,確認作業管理者間隔的收發字節數。

  • 使用ProudNet的內部函數CNetServer::GetStats(CNetServerStats &outVal); 實時獲取每秒收發的流量、發送或接收的數量等。 如果一個客戶端的總流量超過20~30KB,海外服務可能會出現問題。

我建議您使用後刪除NetLimiter等工具。 因Kernel Hooking功能,給握住通信設備的核心帶來20倍以上原本速度的負擔。

事件

- 客戶端與服務器通用

使用參數errorInfoerrorInfo-> ToString();可輕鬆獲得有關問題的信息。

事件註釋

OnError

ProudNet內部發生的Error或因使用過程中出現問題而反饋信息。

OnWarnning

對不嚴重但存在潛在問題的信息進行回電。

OnInformation

對內部情況及溯源等信息進行回電。

OnException

回撥內部Exception錯誤信息。

OnNoRmiProcessed

向PIDL宣佈,但Stub未繼承Event或用戶Return false時呼叫。

- 服務器

可用於性能測試等。

事件註釋

OnUserWorkerThreadBegin

User Worker Thread Pool 的 Thread 啓動時被調用 。

OnUserWorkerThreadEnd

User Worker Thread Pool 的 Thread 結束時被調用 。

Last updated