# 如何使用客戶端

## 定時器循環、RMI、事件處理

根據[**客戶端的主循環**](/proudnet.cn/proudnet-note/notes/main_loop.md#undefined)，客戶端可以定期呼叫<mark style="color:orange;">FrameMove</mark>來執行累積的RMI接收並處理事件處理。

您不一定要每秒調用<mark style="color:orange;">FrameMove</mark>，但如果太長時間不調用，從客戶端接收到的RMI就會累積並增加記憶體使用量。

## 獲取各種資訊

客戶端可以檢索各種訊息，例如P2P群組資訊、P2P連線的客戶端的IP資訊、與伺服器的連線狀態以及是否正在與P2P連線的客戶端執行P2P中繼。

<table data-full-width="true"><thead><tr><th width="342">C++ 函數</th><th width="343">C# 函數</th><th>註釋</th></tr></thead><tbody><tr><td>Proud.CNetClient.GetGroupMembers</td><td>Nettention.Proud.NetClient.NativeNetClient.GetGroupMembers</td><td>檢索與作為參數給出的 HostID 對應的群組中的所有使用者。</td></tr><tr><td>Proud.CNetClient.GetIndirectServerTimeMs</td><td>Nettention.Proud.NetClient.NativeNetClient.GetIndirectServerTimeMs</td><td>取得以 HostID 為參數輸入的對等方的伺服器時間。</td></tr><tr><td>Proud.CNetClient.GetLastUnreliablePingMs</td><td>Nettention.Proud.NetClient.GetLastUnreliablePingMs</td><td>取得以 HostID 為參數輸入的對等方的最後 ping 時間。</td></tr><tr><td>Proud.CNetClient.GetLocalHostID</td><td>Nettention.Proud.NetClient.GetLocalHostID</td><td>取得客戶端的本機 HostID。</td></tr><tr><td>Proud.CNetClient.GetLocalJoinedP2PGroups</td><td>Nettention.Proud.NetClient.GetLocalJoinedP2PGroups</td><td>取得所有加入的 p2p 群組的清單。</td></tr><tr><td>Proud.CNetClient.GetP2PServerTimeMs</td><td>Nettention.Proud.NetClient.NativeNetClient.GetP2PServerTimeMs</td><td>取得 p2p 連線伺服器的時間（以毫秒為單位）。</td></tr><tr><td>Proud.CNetClient.GetPeerInfo</td><td>Nettention.Proud.NetClient.GetPeerInfo</td><td>取得有關連接到客戶端的對等點的資訊。</td></tr><tr><td>Proud.CNetClient.GetPeerReliableUdpStats</td><td>Nettention.Proud.NetClient.NativeNetClient.GetPeerReliableUdpStats</td><td>取得 P2P 之間可靠的訊息系統運行統計資料。 （用於效能測量或調試）</td></tr><tr><td>Proud.CNetClient.GetRecentUnreliablePingMs</td><td>Nettention.Proud.NetClient.GetRecentUnreliablePingMs</td><td>傳回最近的 ping 時間（以毫秒為單位）。</td></tr><tr><td>Proud.CNetClient.GetServerAddrPort</td><td>Nettention.Proud.NetClient.NativeNetClient.GetServerAddrPort</td><td>取得連接的伺服器的位址。</td></tr><tr><td>Proud.CNetClient.GetServerConnectionState</td><td>Nettention.Proud.NetClient.GetServerConnectionState</td><td>取得與伺服器的套接字連線的狀態。</td></tr><tr><td>Proud.CNetClient.GetServerTimeMs</td><td>Nettention.Proud.NetClient.NativeNetClient.GetServerTimeMs</td><td>取得伺服器上的當前時間。</td></tr><tr><td>Proud.CNetClient.GetServerTimeDiffMs</td><td>Nettention.Proud.NetClient.NativeNetClient.GetServerTimeDiffMs</td><td>找出客戶端和伺服器之間的時間差。</td></tr></tbody></table>

## 伺服器連接期間發送和接收自訂數據

* 當您在 <mark style="color:orange;">Connect</mark> 中填寫自訂欄位參數時，\
  內容是從 <mark style="color:orange;">OnConnectionRequest</mark> 接收的。
* 如果在<mark style="color:orange;">OnConnectionRequest</mark>中填入自訂回覆參數，\
  內容是從 <mark style="color:orange;">OnJoinServerComplete</mark> 接收的。

<figure><img src="/files/E92s7ZrpiE9rwXYPfBJE" alt=""><figcaption><p>使用 C++ 解釋客戶端到伺服器連線過程的範例</p></figcaption></figure>

## 取得打孔地址

1. 首先，完成P2P通訊。
2. 使用 <mark style="color:orange;">GetPeerInfo</mark> 或 <mark style="color:orange;">GetClientInfo</mark> 取得客戶端資訊。
3. 取得到的資訊中的<mark style="color:orange;">udpAddrFromServer</mark>為打洞位址。

{% hint style="success" %}
**參考**\
[**P2P 通訊**](/proudnet.cn/proudnet/using_pn/p2p.md)
{% endhint %}

## 取得伺服器時間

ProudNet可讓您從伺服器取得時間以實現主機之間的時間同步。

<table data-full-width="true"><thead><tr><th width="348">C++ 函數</th><th>C# 函數</th><th>註釋</th></tr></thead><tbody><tr><td>Proud.CNetClient.GetServerTimeMs</td><td>Nettention.Proud.NetClient.GetServerTimeMs</td><td>與伺服器計算延遲以獲得伺服器的實際時間。</td></tr><tr><td>Proud.CNetClient.GetP2PServerTimeMs</td><td>Nettention.Proud.NativeNetClient.GetP2PServerTimeMs</td><td>一種取得伺服器延遲、計算其他 P2P 連線客戶端延遲並取平均值的方法。 更準確地說，您可以獲得伺服器上的實際時間。</td></tr></tbody></table>

## 回呼中途斷開

當呼叫 <mark style="color:orange;">FrameMove</mark> 時，將立即呼叫所有累積的事件和接收到的 RMI。

如果使用者願意，ProudNet 能夠在單一 <mark style="color:orange;">FrameMove</mark> 呼叫部分的處理過程中傳回累積事件和接收到的 RMI。 然後，它允許在下一個 <mark style="color:orange;">FrameMove</mark> 呼叫中處理剩餘的累積事件和接收到的 RMI。

\
若要終止剩餘的累積事件和接收的 RMI，請從正在處理 RMI 接收的例程或正在處理事件接收的例程呼叫 <mark style="color:orange;">HolsterMoreCallbackUntilNextFrameMove</mark>。

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

```cpp
DEFRMI_TestS2C_Foo(CMyClient)
{
    ...
    /* 當您呼叫此函數時，CNetClient.FrameMove 立即傳回，
    忽略任何剩餘的累積事件和 RMI 接收。 */
    m_netClient->HolsterMoreCallbackUntilNextFrameMove();
}
 
void CMyClient::OnLeaveServer(...)
{
    ...
    /* 當您呼叫此函數時，CNetClient.FrameMove 立即傳回，
    忽略任何剩餘的累積事件和 RMI 接收。 */
    m_netClient->HolsterMoreCallbackUntilNextFrameMove();
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
simpleStub.Test = (remote, rmiContext, text) => {
    ...
    /* 當您呼叫此函數時，CNetClient.FrameMove 立即傳回，
    忽略任何剩餘的累積事件和 RMI 接收。 */
    netClient.HolsterMoreCallbackUntilNextFrameMove();
}

netClient.LeaveServerHandler = (errInfo) => {
    ...
    /* 當您呼叫此函數時，CNetClient.FrameMove 立即傳回，
    忽略任何剩餘的累積事件和 RMI 接收。 */
    netClient.HolsterMoreCallbackUntilNextFrameMove();
}

```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
**參考**

[**理解主循環**](/proudnet.cn/proudnet-note/notes/main_loop.md)
{% endhint %}

<details>

<summary>保留回呼</summary>

當各種事件和RMI接收被<mark style="color:orange;">FrameMove</mark>回調時，有一個函數允許稍後再次發生所需的回調，稱為**保留回呼**。\
如果您暫停回調，則待處理的回呼將轉到累積接收佇列的後面，並且下次呼叫 <mark style="color:orange;">FrameMove</mark> 時回呼會再次發生。

例如，如果您按住Callback B，則回調將執行如下所示。

FrameMove => Callback A => Callback B (保留!) => 返回

FrameMove => Callback B(回放被擱置的內容) => ... => 返回

如果這個回呼函數沒有被準確理解和誤用，可能會被誤解為接收到的RMI或事件沒有按正確的順序到達或發生無限回呼。 因此，使用時需謹慎。

若要掛起回調，只需從處理 RMI 接收的程式或處理事件接收的例程中呼叫 <mark style="color:orange;">Proud.INetClientEvent.PostponeThisCallback</mark> 或 <mark style="color:orange;">Proud.IRmiStub.PostponeThisCallback</mark> 即可。

```cpp
DEFRMI_TestS2C_Foo(CMyClient)
{
    ...
    /* 當您呼叫此函數時，CNetClient.FrameMove 立即傳回，
    忽略任何剩餘的累積事件和 RMI 接收。 */
    PostponeThisCallback();
}
 
void CMyClient::OnLeaveServer(...)
{
    ...
    /* 當您呼叫此函數時，CNetClient.FrameMove 立即傳回，
    忽略任何剩餘的累積事件和 RMI 接收。 */
    PostponeThisCallback();
}
```

</details>

***

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


---

# 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/using_pn/server_client/using_client.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.
