# 如何使用它

## 獲得主機間延遲(ping time)

作為獲取 ProudNet 中主機之間的延遲(ping time or lag)的方法，\
得到的ping time是roundtriplatency,即從這邊的主機向那邊的主機發送信息後,在那邊的主機立即應答所需的時間。

### **-** 函數從伺服器取得一個客戶端的延遲

<table data-full-width="true"><thead><tr><th>C++ 函數</th><th>C# 函數</th><th>註釋</th><th data-hidden>C++</th><th data-hidden>C#</th></tr></thead><tbody><tr><td>Proud.CNetServer.GetLastPingSec</td><td>Nettention.Proud.NetServer.GetLastReliablePingSec</td><td>以秒爲單位獲取相應客戶端的最後等待時間。</td><td>Proud.CNetServer.GetLastPingSec</td><td></td></tr><tr><td>Proud.CNetServer.GetLastUnreliablePingMs</td><td>Nettention.Proud.NetServer.GetLastUnreliablePingMs</td><td>以Ms爲單位獲取該客戶端的最後等待時間。</td><td>Proud.CNetServer.GetLastUnreliablePingMs</td><td></td></tr><tr><td>Proud.CNetServer.GetRecentPingSec</td><td>Nettention.Proud.NetServer.GetRecentReliablePingSec</td><td>以秒爲單位獲取相應客戶端的最後等待時間。</td><td>Proud.CNetServer.GetRecentPingSec</td><td></td></tr><tr><td>Proud.CNetServer.GetRecentUnreliablePingMs</td><td>Nettention.Proud.NetServer.GetRecentUnreliablePingMs</td><td>以Ms爲單位獲取該客戶端的最後等待時間。</td><td>Proud.CNetServer.GetRecentUnreliablePingMs</td><td></td></tr></tbody></table>

### **-** 從客戶端獲得服務器或連接到P2P的其他客戶端的延遲的函數

<table data-full-width="true"><thead><tr><th width="449">C++ 函數</th><th width="505">C# 函數</th></tr></thead><tbody><tr><td>Proud.CNetClient.GetLastUnreliablePingSec</td><td>Nettention.Proud.NetClient.GetLastUnreliablePingSec</td></tr><tr><td>Proud.CNetClient.GetLastUnreliablePingMs</td><td>Nettention.Proud.NetClient.GetLastUnreliablePingMs</td></tr><tr><td>Proud.CNetClient.GetLastReliablePingSec</td><td>Nettention.Proud.NetClient.GetLastReliablePingSec</td></tr><tr><td>Proud.CNetClient.GetLastReliablePingMs</td><td>Nettention.Proud.NetClient.GetLastReliablePinMs</td></tr><tr><td>Proud.CNetClient.GetRecentUnreliablePingSec</td><td>-</td></tr><tr><td>Proud.CNetClient.GetRecentUnreliablePingMs</td><td>Nettention.Proud.NetClient.GetRecentUnreliablePingMs</td></tr><tr><td>Proud.CNetClient.GetRecentReliablePingSec</td><td>Nettention.Proud.NetClient.GetRecentReliablePingSec</td></tr><tr><td>Proud.CNetClient.GetRecentReliablePingMs</td><td>Nettention.Proud.NetClient.GetRecentReliablePingMs</td></tr></tbody></table>

{% hint style="warning" %}
從<mark style="color:orange;">1.7.40679-master</mark>版本開始，增加了一個新的函數來計算客戶端和伺服器之間的延遲。\
先前的 <mark style="color:orange;">GetLast\~</mark> 或 <mark style="color:orange;">GetRecent\~</mark> 等功能在網路狀況不順暢的情況下，會出現 ping pong 延遲的問題。

如果您可以使用<mark style="color:orange;">1.7.40679</mark>或更高版本，我們建議使用該函數來計算新新增的伺服器的延遲。
{% endhint %}

{% hint style="info" %}
對於Reliable消息傳輸方式，採用雙向通信方式，收發時間比Unreliable慢，但保證收發順序和到達的確定性。
{% endhint %}

{% hint style="info" %}
對於Unreliable消息的傳輸方式，採用單向通信方式，傳輸時間比Reliable快，但傳輸順序可能會出現偏差，無法保證到達的確定性。
{% endhint %}

## 不同語言的程式之間進行通信

兩個程序通過ProudNet進行通信,但有時想用不同的編程語言製作。 在這種情況下，PIDL編譯器可以生成兩種或兩種以上語言的proxy和stub，然後每個程序都拿需要的東西寫下來。

對於int、double、string等基本類型，ProudNet的C++以外語言的Rapping模塊已經提供。 但是,如果語言不同,這種基本類型的名字基本上會有所不同。 例如，C# 的字符串類是 <mark style="color:orange;">System.String</mark>，在 C++ 中是 <mark style="color:orange;">std::string</mark> , <mark style="color:orange;">std::wstring</mark>, <mark style="color:orange;">ATL::CString</mark> , <mark style="color:orange;">Proud::String</mark>。

爲了解決這個問題,PIDL編譯器提供在用戶需要時,將生成的proxy、stub中的變量類型僅限於特定語言的功能。

下面是一個用法範例。

```cpp
// 生成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或事件回撥時，一起接收<mark style="color:orange;">Proud.HostID</mark>以進行識別。 開發遊戲服務器時，如果收到<mark style="color:orange;">Proud.HostID</mark>，就以該值爲依據搜索相應Host的對象，然後處理其對象，ProudNet樣本程序也是以這種方式製作的。

<mark style="color:orange;">Host Tag</mark>是指除HostID外，用戶還可以爲本地主機和其他主機定義的識別數據。

利用這些可以進一步提高程序的性能。

```cpp
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 的示例**

```cpp
DEFRMI_XXX_YYY(MYCLASS)
{
    Host* obj = Lookup(HostID);  // 產生的搜尋費用
    if(obj != NULL)
        obj->Something();
}
```

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/VEHCrzMokH3AgAJteawA/host_tag.png" alt=""><figcaption><p>Host Tag</p></figcaption></figure>

<mark style="color:orange;">Host Tag</mark>被指定爲以下方法。

| C++ 函數                      | C# 函數                                 |
| --------------------------- | ------------------------------------- |
| Proud.CLanClient.SetHostTag | -                                     |
| Proud.CLanServer.SetHostTag | -                                     |
| Proud.CNetClient.SetHostTag | Nettention.Proud.NetClient.SetHostTag |
| Proud.CNetServer.SetHostTag | Nettention.Proud.NetServer.SetHostTag |

{% hint style="warning" %} <mark style="color:orange;">CLanClient</mark> 和 <mark style="color:orange;">CLanServer</mark> 從 <mark style="color:orange;">1.7 版本</mark>開始不支持 。

<mark style="color:orange;">NetClient</mark> 和 <mark style="color:orange;">NetServer</mark> 接手 <mark style="color:orange;">CLanClient</mark> 和 <mark style="color:orange;">CLanServer</mark> 的角色。
{% endhint %}

<mark style="color:orange;">Host Tag</mark>通過<mark style="color:orange;">Proud.RmiContext.m\_hostTag</mark>和<mark style="color:orange;">Proud.CNetPeerInfo.m\_hostTag</mark>接收，不會發生網絡同步，不會傳播給對方。

## 設定Thread Pool

ProudNet的網絡客戶端模塊<mark style="color:orange;">CNetClient</mark>和服務器模塊<mark style="color:orange;">CNetServer</mark>使用兩種[**Thread Pool**](https://docs.proudnet.com/proudnet.cn/proudnet-note/dictionary#thread-pool)。

* <mark style="color:orange;">Networker thread pool</mark>: 僅用於ProudNet內部處理，負責插座I/O處理等。
* <mark style="color:orange;">User worker thread pool</mark>: 用於執行自定義例程 。 處理RMI或回撥活動等

為了獲得最佳效能，請明確建立一個線程池並將其設置在網路模組上，以確保共享特定的線程池，而不是每個擁有這些線程池的模組。

{% hint style="info" %}
當前客戶端使用此功能時，需要include <mark style="color:orange;">ProudNetServer.h</mark>，<mark style="color:orange;">1.7.36365</mark> 後無需include <mark style="color:orange;">ProudNetServer.h</mark>。
{% endhint %}

{% hint style="danger" %}
用戶生成的thread pool客體在網絡模塊全部被破壞之前，如果被破壞，就會發生throw exception。
{% endhint %}

以下是與整體趨勢相關的假代碼。

{% tabs %}
{% tab title="C++" %}

<pre class="language-cpp"><code class="lang-cpp">// 負責線程池開始/結束的回撥的客體。  
<strong>Proud::CThreadPoolEventFunctional e;  
</strong>  
// 即，爲線程池的開始/結束添加一個程序。（可選）
e.OnThreadBeginFunction = [](){...};  
e.OnThreadEndFunction = [](){...};  
  
// 創建具有 CPU 數量的線程的 thread pool 對象 。 
Proud::CThreadPool* p = Proud::CThreadPool::Create(&#x26;e, GetNoofProcessors());  
// 想要2個線程的話
Proud::CThreadPool* p = Proud::CThreadPool::Create(&#x26;e, 2);
 
// ----------用參數傳達登記該thread pool的對象。----------
// 服務器端的情況
<strong>Proud::CStartServerParameter param;
</strong>param.m_externalUserWorkerThreadPool = p;
 
// 客戶端的情況
Proud::CNetConnectionParam param;
// 請參閱在新窗口中打開 ProundNet 線程模型類型鏈接
param.m_userWorkerThreadModel = Proud::ThreadModel::ThreadModel_UseExternalThreadPool;
param.m_externalUserWorkerThreadPool = p;
</code></pre>

{% endtab %}

{% tab title="C#" %}

```csharp
// 想要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);
```

{% endtab %}
{% endtabs %}

線程池設置方法基本與上述相同，根據您想要的Case添加不同的設置值。\
不僅是服務器，客戶端也可以設置多個線程模型。

### - 共享多個網絡模塊相同的Thread Pool

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/N5hlqQgyF9WPeXlYmUhy/share_thread_pool.png" alt=""><figcaption><p>服務器上多個主機模塊相同的Thread Pool共享示例</p></figcaption></figure>

{% tabs %}
{% tab title="C++" %}

<pre class="language-cpp"><code class="lang-cpp">Proud::CStartServerParameter param1;
Proud::CStartServerParameter param2; 
Proud::CStartServerParameter param3; 
 
//使用 Proud::CThreadPool::Create() 創建 thread Pool 對象
<strong>Proud::CThreadPool* p = Proud::CThreadPool::Create(...);  
</strong>  
//共享 user worker thread pool
//Networker thread pool默認設置保持不變，不共享。
param1.m_externalUserWorkerThreadPool = p;
param2.m_externalUserWorkerThreadPool = p;  
param3.m_externalUserWorkerThreadPool = p;
</code></pre>

{% endtab %}

{% tab title="C#" %}

```csharp
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);
```

{% endtab %}
{% endtabs %}

### - 使用不同數量的 Networker 和 User worker thread pool

{% tabs %}
{% tab title="C++" %}

```cpp
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;
```

{% endtab %}

{% tab title="C#" %}

```csharp
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);
```

{% endtab %}
{% endtabs %}

### - 整合 Networker 和 User worker的thread pool

{% tabs %}
{% tab title="C++" %}

```cpp
Proud::CStartServerParameter param;  
Proud::CThreadPool* p = Proud::CThreadPool::Create(..., 8); 
param.m_externalNetWorkerThreadPool = p;  
param.m_externalUserWorkerThreadPool = p;
```

{% endtab %}

{% tab title="C#" %}

```csharp
Nettention.Proud.StartServerParameter param = new Nettention.Proud.StartServerParameter();  
Nettention.Proud.ThreadPool p = new Nettention.Proud.ThreadPool(8); 
param.SetExternalNetWorkerThreadPool(p);  
param.SetExternalUserWorkerThreadPool(p);
```

{% endtab %}
{% endtabs %}

### - 純單線程模型

這是爲了將純粹以單線程運行的服務器升空到CPU數量的方法,需要創建完全沒有線程的線程池客體,並持續呼叫手動讓線程池呼吸的函數。

{% hint style="warning" %}
指定 Networker thread pool 時， Process 或 FrameMove 等不能唱得太慢 。 回電的方法要儘快完成必要的處理。

否則，內部可能會出現ping測量異常或服務器與客戶端之間的連接中斷等。
{% endhint %}

{% tabs %}
{% tab title="C++" %}

```cpp
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);  
    }  
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
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);  
    }  
}
```

{% endtab %}
{% endtabs %}

## 配置 Thread Model - 客戶端

上面分離了user worker和networker，防止發生錯誤，但FrameMove仍有需要明確呼叫的部分。\
這提供了可以在需要時運行累積的RMI的優點。

客戶端的代碼複雜，調用Process或FrameMove時，無需擔心邏輯扭曲或因失誤導致相應代碼遺漏等情況，設定方法是Thread Model設置。\
ProudNet對網絡提供多種操作方法，但也可以設置爲不在意。

### - ProudNet 線程模型類型

> * <mark style="color:orange;">ThreadModel\_SingleThreaded</mark>: \
>   RMI 和消息回撥在用戶呼叫 FrameMove 時完成 。 (默認)
> * <mark style="color:orange;">ThreadModel\_MultiThreaded</mark>: \
>   用戶無需呼叫FrameMove即可完成回撥。
> * <mark style="color:orange;">ThreadModel\_UseExternalThreadPool</mark>: \
>   當指定thread pool時，必須將其設定為此。

以上值可在<mark style="color:orange;">CNetConnectionParam</mark>的<mark style="color:orange;">m\_netWorkerThreadModel</mark>和<mark style="color:orange;">m\_userWorkerThreadModel</mark>中設置。 默認值爲<mark style="color:orange;">ThreadModel\_SingleThreaded</mark>。

{% tabs %}
{% tab title="C++" %}

<pre class="language-cpp"><code class="lang-cpp"><strong>Proud::CNetConnectionParam param;
</strong> 
//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;
</code></pre>

{% endtab %}

{% tab title="C#" %}

```csharp
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;
```

{% endtab %}
{% endtabs %}

## 非同步運行使用者例程

您可以使用 <mark style="color:orange;">Proud.IRmiHost.RunAsync()</mark> 非同步運行函數或 lambda 表達式 A。\ <mark style="color:orange;">Proud.IRmiHost.RunAsync()</mark> 立即返回，A 在線程池中的線程之一上運行。

如果A爲指定的HostID的主機H運行，則H的callback函數和A不能同時運行。

{% tabs %}
{% tab title="C++" %}

```cpp
Proud::CNetServer* s = ...;
Proud::HostID r1 = ...;
string a = ...;
 
s->RunAsync(r1, [a] { WriteSomething(a); });
```

{% endtab %}
{% endtabs %}

## 發送量過多時警告功能

ProudNet的伺服器和網路具有當傳輸量超過線路速度時進行偵測和警告的功能。包含<mark style="color:orange;">ErrorType\_SendQueueIsHeavy</mark> 的 <mark style="color:orange;">Proud.INetCoreEvent.OnWarning</mark> 將被回呼。

發送量過多時,建議改善不必要的信息或[**發送量自動調節功能 (Throttling)**](https://docs.proudnet.com/proudnet.cn/using_pn/tong-xun-xun-xi#throttling)。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.proudnet.com/proudnet.cn/proudnet/usage_pn/usage.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
