# 技術說明

## 在沒有 ProudNet 的情況下， 用單獨的 UDP 套接字進行通信

不建議使用 3rd party 的網絡引擎，但只提供使用 ProudNet [**穿孔**](/proudnet.cn/proudnet-note/dictionary.md#hole-punching)功能的機會。

由此,ProudNet可以獲得客戶端/服務器之間或P2P之間完成的打孔信息,以此爲基礎,可以用與ProudNet分開準備的UDP插座進行通信。

從 ProudNet 獲取的穿孔信息是路由器針對 <mark style="color:orange;">Client</mark> 所擁有的 UDP 套接字映射的信息， 因此必須關閉此 UDP 套接字 。 爲此，必須調用 <mark style="color:orange;">Proud.CNetClient.Disconnect</mark> 或優先調用 <mark style="color:orange;">Client</mark> 對象破壞 。 然後在3rd party中重新使用<mark style="color:orange;">Client</mark>的UDP插座地址。

如果不經過此過程而重複生成UDP插座，則 ProudNet 和 3rd party 的 UDP插座都將異常運行。

主要程序如下。

> 1. 首先，爲了獲取打孔信息，使用<mark style="color:orange;">Proud.CNetClient.GetDirectP2PInfo</mark>。<br>
> 2. 卸載 ProudNet 佔用的 UDP 套接字 。\ <mark style="color:orange;">Proud.CNetClient.InvalidateUdpSocket</mark> 關閉 <mark style="color:orange;">Client</mark> 所擁有的 UDP 套接字 。 此時路由器的打孔狀態保持不變。 <mark style="color:orange;">Proud.CNetClient.InvalidateUdpSocket</mark>中返回的值可用於單獨模塊中的通信。<br>
> 3. 調用<mark style="color:orange;">Proud.CNetClient.InvalidateUdpSocket</mark>後，使用<mark style="color:orange;">Proud.CNetClient.RestoreUdpSocket</mark>新建UDP套接字，恢復服務器與接口之間的UDP通信。<br>
> 4. 有些路由器即使是打孔的，在幾十秒到幾分鐘後，打孔圖可能會被任意改變或蒸發。 ProudNet通過自身的relay fallback功能自行解決該問題。 但是，如果使用3rd party UDP socket，這個問題必須由開發者或3rd party引擎直接解決，否則P2P通信可能會失敗。

<table><thead><tr><th>C++ 函數</th><th width="246">C# 函數</th><th>註釋</th></tr></thead><tbody><tr><td>Proud.CNetClient.GetDirectP2PInfo</td><td>Nettention.Proud.NetClient.GetDirectP2PInfo</td><td>獲取打孔信息以與其他peer通信。</td></tr><tr><td>Proud.CNetClient.InvalidateUdpSocket</td><td>Nettention.Proud.NativeNetClient.InvalidateUdpSocket</td><td>關閉內部擁有的UDP插座後，強制將服務器與P2P通信轉換爲旁路狀態。</td></tr><tr><td>Proud.CNetClient.RestoreUdpSocket</td><td>Nettention.Proud.NativeNetClient.RestoreUdpSocket</td><td>重新創建已移除的 UDP 套接字， 並重新啓動與相對 peer 的 UDP 孔打孔 。</td></tr><tr><td>Proud.CNetClient.Disconnect</td><td>Nettention.Proud.NetClient.Disconnect</td><td>關閉服務器連接，退出所有P2P組。</td></tr></tbody></table>

{% hint style="danger" %}
上述方法不是正常的使用方法。\
因此，對於<mark style="color:orange;">use\_alternative\_p2p</mark>引發的問題，ProudNet不負責。
{% endhint %}

## 在 DLL 項目中使用 ProudNet

使用 ProudNet 作爲 static library， 但如果要用於 DLL 項目， 您必須調用 <mark style="color:orange;">DllMain</mark> 函數中的 <mark style="color:orange;">process detach case</mark> 中的 <mark style="color:orange;">Proud.Thread.NotifyDllProcessDetached</mark> 。 否則程序退出時可能會發生崩潰現象。

```cpp
BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    break;
    case DLL_PROCESS_DETACH:
    {
        Thread::NotifyDllProcessDetached();
    }
    break;
    }
    return TRUE;
}
```

## 將 ProudNet 附加到基於 Web 服務器的遊戲服務器

爲了方便開發遊戲服務器,使用<mark style="color:orange;">web application server (WAS)</mark>,但缺點是很難快速處理實時多人在服務器上處理遊戲,服務器負荷過大。

爲了彌補這一點,將像ProudNet這樣的插座服務器和WAS混合使用的方法如下。

### (1) 如何在套接字服務器和WAS之間建立數據庫，實現數據共享

雖然直觀、簡單,但缺點是處理量集中在數據庫上,處理超過數據共享,WAS無法向套接字服務器發送請求和接收響應。

<figure><img src="/files/TnboEPm3wnrfblWGndz9" alt=""><figcaption></figcaption></figure>

### (2) 套接字服務器具有處理HTTP請求應答的功能，WAS向套接字服務器發送HTTP請求的方法

爲此,有將Microsoft REST SDK等用於套接字服務器的方法。

<figure><img src="/files/Nqynuf0snVVAWJh10Ut3" alt=""><figcaption></figcaption></figure>

### (3) 如何在 WAS 和套接字服務器之間進行非 HTTP 的套接字通信

WAS向插件發送命令，插件接收命令後發送到套接字服務器。 對於 ProudNet，可以使用 <mark style="color:orange;">NetClient</mark> 或 <mark style="color:orange;">LanClient</mark>。

#### WAS 的插件編程方法

> <mark style="color:orange;">PHP</mark>: CGI or FastCGI\ <mark style="color:orange;">ASP.NET</mark>: C# class library\ <mark style="color:orange;">node.js</mark>: C plugin\ <mark style="color:orange;">Java</mark>: JNI

## 創建不良網絡環境

用阻止中途UDP通信的方法模仿惡劣的網絡環境。 通過調用 <mark style="color:orange;">Proud.CNetClient.TEST\_FallbackUdpToTcp</mark>，可以暫時或永久地阻止 UDP 通信。

## TCP 延遲傳輸功能和 Nagle 算法

TCP基本上內置了延遲發送功能,這可以在WAN網絡通信時有效地維持通信量。 但是需要發送的數據稍微延遲後發送給對方。

延遲發送功能也被稱爲<mark style="color:orange;">Nagle算法</mark>。 <mark style="color:orange;">Nagle算法</mark>會延遲0.01\~0.7秒的發送，因此可能不適合網絡遊戲，因此ProudNet提供關閉功能。 <mark style="color:orange;">Nagle算法</mark>關閉時，Delayed Send最長爲0.01秒，以防止<mark style="color:orange;">Silly Window Syndrome</mark>。

如何控制<mark style="color:orange;">Nagle算法</mark>，請參考以下內容。

<table data-full-width="true"><thead><tr><th width="500">C++ 函數</th><th>註釋</th></tr></thead><tbody><tr><td>Proud.CStartServerParameter.m_enableNagleAlgorithm</td><td>啓用或禁用 TCP 延遲傳輸和 Nagle 算法 。</td></tr><tr><td>Proud.CStartLanServerParameter.m_enableNagleAlgorithm</td><td>-</td></tr></tbody></table>

## Apple的IPv6策略迂迴策略

由於Apple的策略，連接服務器時不能使用以下格式的地址。

```
11.22.33.44
1122:3344:5566:7788:1122:3344:5566:7788
```

相反，您必須使用 FQDN 格式的主機名。

```
myserver1.mygame.com
```

雖然要儘快應對Apple的政策，但是如果沒有時間立即對所有服務器分配FQDN名稱的話，可以使用ProudNet提供的迂迴解決的方法。

除了想要連接的服務器地址外，在\[1]和\[2]中放入不同的FQDN即可。

```cpp
CNetClient* nc = CNetClient::Create();
p.m_serverIP = "11.22.33.44";
p.m_publicDomainName1 = "www.nettention.com"; // [1]
p.m_publicDomainName2 = "www.nts.go.kr";  // [2]
nc->Connect(p);
```

{% hint style="warning" %}
**注意**

* \[1]和\[2]必須是不同的域名。
* \[1]科 \[2] 不應該具有 IPv6 地址。
* <mark style="color:orange;">NetClient</mark> 不會實際訪問 \[1] 和 \[2] 的服務器。 但是\[1]和\[2]必須是有效主機。
  {% endhint %}

{% hint style="danger" %}
本迂迴策略不是根本的解決方法，並不能保證所有<mark style="color:orange;">NetServer</mark>的正常連接。 爲了從根本上解決這一問題，必須按照Apple的建議使用FQDN格式的主機名稱。
{% endhint %}

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

&#x20;[**IPv4 分配結束**](https://namu.wiki/w/IP#s-2.1.2)
{% endhint %}

## Unicode-多字節相互轉換

ProudNet使用<mark style="color:orange;">Unicode</mark>，因此建議開發時使用Unicode，但即使是不使用Unicode的程序，也可以使用ProudNet。

無法使用Unicode時（現有製作的程序已不是Unicode基礎等），將輸入到ProudNet的字符串轉換爲Unicode的過程和輸出的字符串轉換爲多字節代碼<mark style="color:orange;">MBCS</mark>。

此作用的類別是 <mark style="color:orange;">Proud::StringA2W</mark>, <mark style="color:orange;">Proud::StringW2A</mark> 。

<mark style="color:orange;">Proud::StringA2W</mark>類負責將<mark style="color:orange;">MBCS</mark>轉化爲Unicode，而<mark style="color:orange;">Proud::StringW2A</mark>則反過來將Unicode轉化爲<mark style="color:orange;">MBCS</mark>。

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

```cpp
void Foo()
{
    // 從 Unicode 轉換爲 MBCS
    Proud::String a=L"ABC";
    const char* b = Proud::StringW2A(a);
    
    // 將 MBCS 轉換為 Unicode
    Proud::String c = Proud::StringA2W(b);
}
```

{% endtab %}
{% endtabs %}


---

# 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-note/notes.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.
