> 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-note/sample.md).

# Sample 例題

以下示例是根據<mark style="color:orange;">git 樣本項目</mark>創建的。 詳細內容請參考以下鏈接。

:open\_file\_folder: [**C++ 下載示例**](https://github.com/Nettention/ProudNet_Sample/tree/main/Simple)&#x20;

:open\_file\_folder: [**C# 下載示例**](https://github.com/Nettention/ProudNet_Sample/tree/main/SimpleCSharp)

## 客戶

### - Proxy & Stub

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

<pre class="language-cpp"><code class="lang-cpp"><strong>// 客戶端-> 服務器 RMI Proxy 實例
</strong><strong>Simple::Proxy g_SimpleProxy;
</strong>
// 與RMI proxy不同,在函數重疊後使用。
class SimpleStub : public Simple::Stub
{
public:
	DECRMI_Simple_ShowChat;
	DECRMI_Simple_SystemChat;

	DECRMI_Simple_P2PChat;
};

// 接收消息的 RMI stub 實例
SimpleStub g_SimpleStub;
</code></pre>

{% endtab %}

{% tab title="C#" %}

```csharp
using namespace Nettention.Proud;

// RMI proxy是用來發送信息的。
// 函數調用在不同的流程中運行。
static Simple.Proxy g_Proxy = new Simple.Proxy();

// RMI stub是用來接收信息的。
static Simple.Stub g_Stub = new Simple.Stub();
```

{% endtab %}
{% endtabs %}

### - 定義 RMI 函數

{% tabs %}
{% tab title="C++" %}
RMI函數爲了便於命名，使用以下規則。

\=> <mark style="color:orange;">DEFRMI\_GlobalName\_FunctionName</mark>

```cpp
DEFRMI_Simple_P2PChat(SimpleStub)
{
    ...

    // 沒有特別的意義， 但必須返回 true 。
    return true;
}

DEFRMI_Simple_ShowChat(SimpleStub)
{
    ...
    return true;
}

DEFRMI_Simple_SystemChat(SimpleStub)
{
    ...
    return true;
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 定義接收各Stub函數時運行的功能的函數
g_Stub.P2PChat = (...) =>
{
    ...
    return true;
}
    
g_Stub.ShowChat = (...) =>
{
    ...
    return true;
}
    
g_Stub.SystemChat = (...) =>
{
    ...
    return true;
}
```

{% endtab %}
{% endtabs %}

### - 創建 NetClient 對象&#x20;

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

```cpp
std::shared_ptr<Proud::CNetClient> netClient(Proud::CNetClient::Create());
```

{% endtab %}

{% tab title="C#" %}

```csharp
Nettention.Proud.NetClient netClient = new Nettention.Proud.NetClient();
```

{% endtab %}
{% endtabs %}

### - 事件鏈接&#x20;

{% tabs %}
{% tab title="C++" %}
\
在服務器連接活動中設計所需的邏輯後使用即可。

```cpp
// 服務器連接完成後調用的事件
netClient->OnJoinServerComplete = 
   [&](ErrorInfo *info, const ByteArray &replyFromServer)
{
    ...
}
    
// 服務器斷開後要執行的事件
netClient->OnLeaveServer = [&](ErrorInfo *errorInfo)
{
    ...
}
    
// 新建 p2p 連接時發生的事件
// memberHostID : 連接到 p2p 的客戶端用戶名
// groupHostID : 連接到 p2p 的組用戶名
netClient->OnP2PMemberJoin = 
    [&](HostID memberHostID, HostID groupHostID,int memberCount, const ByteArray &customField)
{
    ...
}
    
// p2p 連接中斷時發生的事件 
netClient->OnP2PMemberLeave = 
    [](HostID memberHostID, HostID groupHostID,int memberCount)
{
    ...
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 服務器連接完成後調用的事件
netClient.JoinServerCompleteHandler = (info, replyFromServer) =>
{
    ...
};

// 服務器斷開時運行的邏輯
netClient.LeaveServerHandler = (errorInfo) =>
{
    ...
};

// 在p2p組中添加新成員時執行的logic
netClient.P2PMemberJoinHandler = 
    (memberHostID, groupHostID, memberCount, customField) =>
{
    ...
};

// p2p成員連接中斷時執行的logic
netClient.P2PMemberLeaveHandler = (memberHostID, groupHostID, memberCount) =>
{
    ...
};
```

{% endtab %}
{% endtabs %}

### - 註冊 Proxy & Stub

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

```cpp
// 在CNetClient註冊用戶創建的Proxy和Stub
netClient->AttachProxy(&g_SimpleProxy);
netClient->AttachStub(&g_SimpleStub);
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 將 proxy 和 stub 連接到 NetClient 實例
netClient.AttachProxy(g_Proxy);	    // Client-to-server => 從客戶端到服務器
netClient.AttachStub(g_Stub);	   // server-to-client => 從服務器到客戶端
```

{% endtab %}
{% endtabs %}

### - 服務器連接

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

```cpp
// 配置啓動服務器所需的 Parameter
Proud::CNetConnectionParam cp;

// 需要輸入像服務器一樣的協議版本。 也有可能乾脆不輸入。
cp.m_protocolVersion = g_Version;
cp.m_closeNoPingPongTcpConnections = false;
cp.m_serverPort = g_ServerPort;	

// 開始連接服務器
// 此函數立即return 。
// 之前在後臺嘗試連接，
// 結果將通過OnJoin Server Complete活動告知。
netClient->Connect(cp);
```

此後， 調用每幀的函數都會調用 <mark style="color:orange;">netClient</mark> -> <mark style="color:orange;">FrameMove()</mark> 方法。

```cpp
// 例子
while (true)
{
    netClient->FrameMove();
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 配置啓動服務器所需的 Parameter
Nettention.Proud.NetConnectionParam cp = 
    new Nettention.Proud.NetConnectionParam();

// 與服務器相同的protocol version
cp.protocolVersion.Set(SimpleCSharp.Vars.m_Version);

// server address
cp.serverIP = "localhost";

// server port
cp.serverPort = (ushort)SimpleCSharp.Vars.m_serverPort;

// 服務器連接開始
// 這個函數立即返回。
// 之前在後臺嘗試連接，
// 通過OnJoin Server Complete活動可以知道連接完成。
netClient.Connect(cp);
```

隨後， 調用 <mark style="color:orange;">netClient.FrameMove()</mark> 方法來調用每幀調用函數 。

```csharp
// 例子
while (true)
{
    netClient.FrameMove();
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
基本上，您必須執行 <mark style="color:orange;">FrameMove</mark> 才能使事件發生。
{% endhint %}

## 伺服器

### - Proxy & Stub

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

```cpp
// 用於從服務器傳輸到客戶端的 RMI Proxy
Simple::Proxy g_SimpleProxy;


// 與RMI proxy不同,在函數重疊後使用。
class SimpleStub : public Simple::Stub
{
public:
	DECRMI_Simple_Chat;
};

// 服務器 -> 客戶端 RMI Stub 實例
SimpleStub g_SimpleStub;
```

{% endtab %}

{% tab title="C#" %}

```csharp
using namespace Nettention.Proud;

// 用於接收來自客戶端的消息的 RMI Stub
static Simple.Stub g_Stub = new Simple.Stub();

// RMI proxy
static Simple.Proxy g_Proxy = new Simple.Proxy();
```

{% endtab %}
{% endtabs %}

### - 定義 RMI 函數

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

<pre class="language-cpp"><code class="lang-cpp"><strong>DEFRMI_Simple_Chat(SimpleStub)
</strong>{
    ...
    return true;
}
</code></pre>

{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp"><code class="lang-csharp">// 定義客戶端收到聊天消息時運行的邏輯 。
<strong>g_Stub.Chat = (...) =>
</strong>{
    ...
    return true;
};
</code></pre>

{% endtab %}
{% endtabs %}

### - 創建 NetServer 對象

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

```cpp
std::shared_ptr<Proud::CNetServer> srv(Proud::CNetServer::Create());
```

{% endtab %}

{% tab title="C#" %}

```csharp
Nettention.Proud.NetServer srv = new Nettention.Proud.NetServer();
```

{% endtab %}
{% endtabs %}

### - 事件鏈接

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

```cpp
// 設置客戶端連接服務器時運行的logic。
srv->OnClientJoin = 
    [](CNetClientInfo* clientInfo, ErrorInfo* errorInfo, const ByteArray& comment)
{
    ...
};

// 設置客戶端服務器斷開時運行的邏輯 。
srv->OnClientLeave = 
    [](CNetClientInfo *clientInfo, ErrorInfo *errorInfo, const ByteArray& comment)
{
    ...
};
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 設置客戶端連接服務器時運行的logic。
srv.ClientJoinHandler = (clientInfo) =>
{
    ...
};

// 設置客戶端服務器斷開時運行的邏輯 。
srv.ClientLeaveHandler = (clientInfo, errorInfo, comment) =>
{
    ...
};
```

{% endtab %}
{% endtabs %}

### - 註冊 Proxy & Stub

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

```cpp
// 在生成的CNetServer實例上註冊proxy和stub。
srv->AttachStub(&g_SimpleStub);
srv->AttachProxy(&g_SimpleProxy);
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 在生成的CNetServer實例上註冊proxy和stub。
srv.AttachStub(g_Stub);
srv.AttachProxy(g_Proxy);
```

{% endtab %}
{% endtabs %}

### 啓動服務器

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

```cpp
Proud::CStartServerParameter p1;

// This must be the same to the client. => 必須和客戶端一樣 。
p1.m_protocolVersion = g_Version; 

// TCP listening endpoint => tcp 連接進來 port
p1.m_tcpPorts.Add(g_ServerPort); 

/* 啓動服務器
該函數在失敗時會產生exception。
如果不把threading model具體化的話
通過信息發送的RMI函數和活動回撥是從分開的thread pool中呼叫的。
可以單獨指定thread model,相關部分可以參考幫助內容。 https://guide.nettention.com/cpp_ko#thread_pool_sharing
*/
srv->Start(p1);
```

{% endtab %}

{% tab title="C#" %}

```csharp
var p1 = new Nettention.Proud.StartServerParameter();

// This must be the same to the client. => 必須和客戶端一樣 。
p1.protocolVersion = new Nettention.Proud.Guid(Vars.m_Version); 

// TCP listening endpoint => tcp 連接進來 port
p1.tcpPorts.Add(Vars.m_serverPort);

/* 啓動服務器
該函數在失敗時會產生exception。
如果不把threading model具體化的話
通過信息發送的RMI函數和活動回撥是從分開的thread pool中呼叫的。
可以單獨指定thread model,相關部分可以參考幫助內容。 https://guide.nettention.com/cpp_ko#thread_pool_sharing
*/
srv.Start(p1);
```

{% endtab %}
{% endtabs %}

## 共通

### - vars.h

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

```cpp
extern Proud::Guid g_Version;
extern int g_ServerPort;
```

{% endtab %}
{% endtabs %}

### - vars.cpp, vars.cs

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

```cpp
// 定義協議版本
// 服務器和客戶端都必須具有相同的值。
PNGUID guid = { 0x3ae33249, 0xecc6, 0x4980, { 0xbc, 0x5d, 0x7b, 0xa, 0x99, 0x9c, 0x7, 0x39 } };
Guid g_Version = Guid(guid);

// TCP listening port number.
int g_ServerPort = 33334;
```

{% endtab %}

{% tab title="C#" %}

```csharp
namespace SimpleCSharp
{
    public class Vars
    {
        // 同樣適用於服務器和客戶端的protocol version
        public static System.Guid m_Version = new System.Guid("{ 0x3ae33249, 0xecc6, 0x4980, { 0xbc, 0x5d, 0x7b, 0xa, 0x99, 0x9c, 0x7, 0x39 } }");
        public static int m_serverPort = 33334;
    }
}
```

{% endtab %}
{% endtabs %}
