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

# 技術說明

## 在沒有 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 %}
