> For the complete documentation index, see [llms.txt](https://docs.proudnet.com/proudnet.cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.proudnet.com/proudnet.cn/proudnet/using_pn/p2p/using_p2p.md).

# 如何使用P2P通訊

## 簡單的使用方法

透過添加簡單的程式碼就可以進行P2P通訊。

### - PIDL 新增範例

詳細使用範例請參考[PIDL](/proudnet.cn/proudnet/using_pn/pidl.md)。

```
//File Name C2C – 這是客戶端之間 
//協定的定義檔。

// client-to-client RMI, 
// 第一條訊息ID = 4000
global C2C 4000 
{
    P2PChat ([in] Proud::StringA txt);
}
```

### - 伺服器

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

<pre class="language-cpp"><code class="lang-cpp">Proud::CNetServer *srv 
         = Proud::CNetServer::Create();
<strong>Proud::HostID groupHostID;
</strong></code></pre>

&#x20;建立P2P組

```cpp
// 警告！ 不要在實際伺服器上 
// 使用GetClientHostIDs， 
// 而是單獨管理它。
Proud::HostID list[100];
int listCount = 
        srv->GetClientHostIDs(
             list, 
             100);
  
groupHostID = 
        srv->CreateP2PGroup(
             list, 
             listCount, 
             ByteArray());
```

&#x20;

&#x20;向 P2P 組發送通信

```cpp
g_S2CProxy.P2PChat(
         groupHostID, 
         Proud::RmiContext::ReliableSend,
         L"Hello~~~!");
```

&#x20;

銷毀P2P組

```cpp
srv->DestroyP2PGroup(groupHostID);
```

{% endtab %}

{% tab title="C#" %}
對象

<pre class="language-csharp"><code class="lang-csharp"><strong>Nettention.Proud.NetServer srv = new Nettention.Proud.NetServer();
</strong>Nettention.Proud.HostID groupHostID;
</code></pre>

&#x20;

建立P2P組

<pre class="language-csharp"><code class="lang-csharp">// 警告！ 不要在實際伺服器上 
// 使用GetClientHostIDs， 
// 而是單獨管理它。
<strong>Nettention.Proud.HostID[] list = srv.GetClientHostIDs();
</strong>
groupHostID = srv.CreateP2PGroup(
             list,
             new Nettention.Proud.ByteArray());
</code></pre>

向 P2P 組發送通信

```csharp
g_S2CProxy.P2PChat(
         groupHostID, 
         Nettention.Proud.RmiContext.ReliableSend,
         "Hello~~~!");
```

銷毀P2P組

```csharp
srv.DestroyP2PGroup(groupHostID);
```

{% endtab %}
{% endtabs %}

### - 客戶

除了額外使用的部件之外，將其省略。

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

```cpp
// Header File 添加
#include “../C2C_Stub.h”
// CPP File 添加
#include “../C2C_Stub.cpp”
```

&#x20;

&#x20;**需要添加到Server & Client端結構的部分**

```
client ->AttachProxy(
         &g_C2CProxy);
client ->AttachStub(
         &g_C2CStub);
```

&#x20;

**Event 對象 - 額外使用的部分**

```cpp
Class CClientEventSink : public INetClientEvent
{
       // 它被連續調用 
       // 與新添加的組成員的 
       // 數量一樣多。
       virtual void OnP2PMemberJoin(
           HostID memberHostID, 
           HostID groupHostID, 
           int memberCount, 
           const ByteArray &customField) {}
  
       // 當許多成員被刪除時， 
       // 它會被不斷調用。
       virtual void OnP2PMemberLeave(
           HostID memberHostID, 
           HostID groupHostID, 
           int memberCount) {}
  
       // 其他省略
}
```

**用於接收C2C通訊的對象**

```cpp
Class C2CStub 
         : public C2C::Stub
{
DECRMI_C2C_P2PChat;
}
DEFRMI_C2C_P2PChat(C2CStub)
{
   Printf(
          “[client] %d, %s”, 
          remote, 
          txt);
}
C2CStub g_C2CStub;
```

&#x20;

**用於發送C2C通訊的對象**

```cpp
C2C::Proxy g_C2CProxy;
```

{% endtab %}

{% tab title="C#" %}
**namespace**

```csharp
using namespace ChatC2C;
```

&#x20;**需要添加到Server & Client端結構的部分**

```csharp
//將 proxy 和 stub attach到 client 。
//警告！！！ 如果不附加的話就不起作用。
netClient.AttachProxy(c2cProxy);
netClient.AttachStub(c2cStub);
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
對於頻繁發送和丟失的信息，如Move Packet，使用Unreliable更有效。

* **已完成打孔的客戶端**\
  它內部使用可靠的UDP，但會發生重傳，這可能會增加流量。<br>
* **未打孔的客戶端**\
  透過伺服器進行中繼通信，此時Unreliable使用UDP與伺服器通信，Reliable使用TCP通訊。
  {% endhint %}

## 減少 Relay 通信時的 P2P 消息總量

透過<mark style="color:orange;">Proud.CNetClient.GetPeerInfo</mark>，您可以找出將接收RMI呼叫的另一個客戶端是否正在與您進行直接P2P通訊。 如果您使用中繼通信，有一種方法可以減少每秒傳輸角色位置 8 次的 RMI 調用次數到每秒 4 次。

## JIT(Just-in-time) P2P 連接

ProudNet 被設計為僅在必要時執行點對點打洞。&#x20;

例如，即使實際上使用 <mark style="color:orange;">Proud.CNetServer.JoinP2PGroup()</mark> 在兩個對等點之間建立了連接，當使用者在兩個對等點之間發起 P2P 訊息傳遞時，也會發生實際的打洞。

這些功能具有預防NAT路由器port mapping entry過多現象的效果。

如果要在兩個對等點之間建立連線後立即執行 P2P 打洞，請在 <mark style="color:orange;">Proud.CNetServer.SetDirectP2PStartCondition()</mark> 中選擇 Always 值而不是 JIT。

## 在服務器上對多個客戶端進行 routed multicast

ProudNet利用P2P功能在MMO遊戲中實現有效的組播。\ <mark style="color:orange;">server-to-client routed multicast</mark>是利用P2P功能在客戶端進行服務器通信Relay的功能，利用該功能可以減少服務器產生的流量。

<figure><img src="/files/KSLFTPkvvVWWQoDYTA12" alt=""><figcaption><p>服務器- 客戶端之間的 routed multicast</p></figcaption></figure>

只有滿足以下條件，才能進行<mark style="color:orange;">server-to-client routed multicast</mark>。

> * 必須是[**Unreliable 消息**](/proudnet.cn/proudnet-note/dictionary.md#unreliable)。
> * 爲了[**Multicast**](/proudnet.cn/proudnet-note/dictionary.md#undefined-1)，必須是一次呼叫。\
>   如果爲發送給多個主機而爲每個目標分別調用 RMI，則無法執行<mark style="color:orange;">server-to-client routed multicast</mark>。
> * <mark style="color:orange;">Proud.RmiContext</mark>的參數成員<mark style="color:orange;">Proud.RmiContext.m\_unreliableS2CRoutedBroadcastMaxCount</mark>必須填寫。
> * 如有需要，還需填寫 <mark style="color:orange;">Proud.RmiContext.m\_unreliableS2CRoutedBroadcastMaxPing</mark>。預設：預設值
> * 必須事先在P2P之間進行過通信或將P2P連接條件從<mark style="color:orange;">JIT</mark>改爲<mark style="color:orange;">always</mark>。

## 將伺服器包含為 P2P 群組成員

您可以將伺服器作為群組成員新增至 P2P 群組中，當您也想將 P2P 群組的訊息傳送到伺服器時，這非常有效。

RMI stub不僅要連接客戶端，還要連接服務器方面。

例如，當存在名爲X的RMI函數組時，X:Proxy通過<mark style="color:orange;">Proud.CNetClient.AttachProxy</mark>連接，X:Stub通過<mark style="color:orange;">Proud.CNetClient.AttachStub</mark>, <mark style="color:orange;">Proud.CNetServer.AttachStub</mark>連接，服務器才能接收發送給P2P組的RMI呼叫。\
然後，您必須將 <mark style="color:orange;">Proud.CStartServerParameter.m\_allowServerAsP2PGroupMember</mark> 設定為'true'並啟動伺服器以允許伺服器成為 P2P 群組的成員。<br>

建立P2P群組或新增成員至現有P2P群組時，如果輸入<mark style="color:orange;">Proud.HostID\_Server</mark>作為參數，則伺服器也會成為P2P群組的成員。

## 以 SuperPeer 爲中心的 P2P 網絡

在小型多人線上遊戲（MO 或休閒）遊戲中，點對點之間進行溝通的方法之一是 P2P 組的一名成員負責發送和接收遊戲的所有訊息。\
這種方式也稱為以<mark style="color:orange;">Super Peer(超級對等點或主機)為中心的 P2P網絡</mark>。

在以<mark style="color:orange;">Super Peer 為中心的 P2P 網絡</mark>方式中，在遊戲進行過程中，每個peer都不會將要發送的信息直接發送給P2P組的所有成員。 相反，它會向指定的 1 個 SuperPeer 發送消息， 並將消息直接發送給 P2P 組中的所有其他成員 。

<figure><img src="/files/mKjs4RGytnzAlFHQnchU" alt=""><figcaption><p>Super Peer 例子</p></figcaption></figure>

在P2P組的客戶端中，如果網路環境很好的客戶端作為超級節點是有效的。 此時，超級對等客戶端收到的流量與其他客戶端的數量一樣多，收到的流量與其他客戶端數量的平方一樣多。

然而，如果P2P組中的客戶端都沒有良好的通訊速度，則遊戲品質可能會下降。 因此，在實現以超級節點為中心的P2P組網時，必須慎重選擇超級節點的通訊品質。

### - 如何選擇 Super Peer

> * 直接連接到網絡線路而不是路由器上的peer
> * 高傳輸速率peer
> * 性能好、 每秒運行幀率高的peer

### - Super Peer執行效能和流量考慮

使用SuperPeer的目的之一是借調服務器難以承受的運算量,並實施客戶端之一。 進行物理引擎運算並直接影響其運算結果的遊戲玩法，建議由SuperPeer代理處理。<br>

ProudNet爲了判斷Super Peer的執行性能，使用<mark style="color:orange;">frame rate</mark>。\
為此，您需要執行以下操作：

> * 在 SuperPeer 選定過程中設置 <mark style="color:orange;">Proud.CSuperPeerSelectionPolicy.m\_frameRateWeight</mark> 或使用 <mark style="color:orange;">Proud.CSuperPeerSelectionPolicy.GetOrdinary()</mark> 方法。
> * 通過 <mark style="color:orange;">Proud.CNetClient.SetApplicationHint</mark> 將應用程序中測量的frame rate傳遞給 <mark style="color:orange;">Proud.CNetClient</mark> 。

{% hint style="warning" %}
家庭網路每秒的傳輸量一般不超過30KB至200KB，且差異較大。

由於Super Peer要求的每秒傳輸速率比其他peer高很多，因此當選擇其作為家庭上網的電腦時，需要控制通訊音量，為此請參考[**發送量自動調節功能 (throttling)**](/proudnet.cn/proudnet/using_pn/tong-xun-xun-xi.md#throttling)。
{% endhint %}

***

## :arrow\_left: [**返回**](/proudnet.cn/proudnet/using_pn/p2p.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.proudnet.com/proudnet.cn/proudnet/using_pn/p2p/using_p2p.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
