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

# 伺服器和客戶端

## 啟動伺服器

要啟動伺服器，首先需要取得伺服器對象，如下例所示。 伺服器建立後，不會立即建立與客戶端或執行緒池的通信，因此必須透過在建立的物件上呼叫 <mark style="color:orange;">Start</mark> 來運行伺服器。

### 1. 創建伺服器

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

<pre class="language-cpp"><code class="lang-cpp"><strong>m_netServer = Proud::CNetServer::Create();
</strong>// 可以使用delete運算子刪除已修復的物件。
delete m_netServer;
</code></pre>

{% endtab %}

{% tab title="C#" %}

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

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
在啟動伺服器之前，請務必檢查伺服器的[**伺服器的UDP連接埠類型**](/proudnet.cn/proudnet/usage_pn/tips.md#udp)。
{% endhint %}

### 2. 準備

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

```cpp
// 將 ProudNet 進行 include 。
#include “include\ProudNetServer.h”
  
// 在 ProudNet 中，所有物件都被分組到一個名為 Proud 的namespace中。
using namespace Proud;
  
// port 定義
int g_ServerPort = 33334;
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 添加至使用Proud
using Nettention.Proud;

// 預先定義要使用的port
int serverPort = 33334;
```

{% endtab %}
{% endtabs %}

### 3. 啟動伺服器

先建立一個伺服器對象，然後呼叫<mark style="color:orange;">SetEventSink</mark>函數。 這是註冊對像以接收伺服器上發生的事件的回調的過程。 如果將指針傳遞給透過繼承 <mark style="color:orange;">INetServerEvent</mark> 物件建立的對象，伺服器將透過該物件回呼事件。

{% hint style="info" %}
從 C++11 開始，可以使用 lambda 而不是 <mark style="color:orange;">SetEventSink</mark> 來註冊事件。
{% endhint %}

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

```cpp
// 請僅參考 C++11 之前的版本。
// g_eventSink是繼承INetServerEvent的物件。
CNetServer* srv = 
         Proud::CNetServer::Create();
srv->SetEventSink(
         &g_eventSink);
  
// 設定啟動伺服器所需的參數。
CStartServerParameter p1;
 
// 接收Client端Connection的Port
p1.m_tcpPort = 33334;  
  
srv->Start(p1);
```

{% endtab %}

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

```cpp
// 可以透過註冊要在特定事件中作為 lambda 執行的邏輯來使用它。
srv->OnClientJoin = [...](CNetClientInfo* clientInfo) {
        // my event handler
        ...
};
```

{% endtab %}

{% tab title="C#" %}

```csharp
using namespace Nettention.Proud;
// NetServer, StartServerParameter 省略命名空間

NetServer netServer = new NetServer();

netServer.ClientJoinHandler = (clientInfo) => {
    // 當客戶端連接到伺服器時
};

// 登記附加活動
...

StartServerParameter p1 = new StartServerParameter();
// 無需設定protocolVersion即可使用。
p1.protocolVersion = "與客戶端相同的 protocolVersion";
// 輸入上面註冊的連接埠號碼或直接輸入。
p1.tcpPorts.Add(serverPort);

netServer.Start(p1);
```

{% endtab %}
{% endtabs %}

:bulb:在上面的程式碼範例中，<mark style="color:orange;">g\_eventSink</mark> 是使用下列結構建立的物件。

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

<pre class="language-cpp"><code class="lang-cpp"><strong>// 如何建立一個物件來接收來自 CNetServer 的事件
</strong><strong>Class CServerEventSink 
</strong>         : public INetServerEvent 
{
       // 當客戶端連線完成時 
       // 會發生回呼。
       // 它接收 CNetClientInfo 物件作為參數。
       Virtual void OnClientJoin(
           CNetClientInfo *info) 
           OVERRIDE
       {
           // 我們接收 
           // 並處理客戶的資訊。
       }
       // 當客戶端斷開連線時 
       // 會發生回呼。
           Virtual void OnClientLeave(
               CNetClientInfo *info) 
               OVERRIDE
       {
               // 我們接收 
               // 並處理客戶的資訊。
       }
       // 其餘事件省略。
}
</code></pre>

{% endtab %}

{% tab title="C#" %}

```csharp
// 在 C# 中，您可以使用事件處理程序，而無需建立單獨的事件物件。

// 當客戶端連接到伺服器時執行。
netServer.ClientJoinHandler = (clientInfo) =>
{
    Console.Write("Client {0} connected.\n", clientInfo.hostID);
};

// 當客戶端伺服器連線遺失時執行。
netServer.ClientLeaveHandler = (clientInfo, errorInfo, comment) =>
{
    Console.Write("Client {0} disconnected.\n", clientInfo.hostID);
};
```

{% endtab %}
{% endtabs %}

<mark style="color:orange;">CNetClientInfo</mark>物件包含連線的客戶端訊息，<mark style="color:orange;">CNetClientInfo</mark>的成員<mark style="color:orange;">m\_HostID</mark>是可以識別每個主機的ID值。

:bulb:C# 中的 <mark style="color:orange;">NetClientInfo</mark> 與 C++ 中的 <mark style="color:orange;">CNetClientInfo</mark> 的作用相同。

### 4. 解除連接

<table><thead><tr><th width="313">函數</th><th>註釋</th></tr></thead><tbody><tr><td>Stop</td><td>伺服器停止了。 斷開所有連線。</td></tr><tr><td>CloseConnection(Client的 HostID)</td><td>中斷與對應客戶端的連線。</td></tr></tbody></table>

### 5. 開始接收客戶端連接

為了接收來自伺服器的客戶端連接，需要在伺服器端準備一個<mark style="color:orange;">Listening Port</mark>和執行緒池。 為此，我們需要建立一個 <mark style="color:orange;">Server</mark> 對象，然後呼叫其 <mark style="color:orange;">Start</mark> 方法。

## 開始使用客戶端

與伺服器一樣，客戶端也可以在建立物件後連接到伺服器。

### 1. 創建客戶端

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

```cpp
m_netClient = Proud::開始接收客戶端連接CNetClient::Create();
```

{% endtab %}

{% tab title="C#" %}

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

{% endtab %}
{% endtabs %}

### 2. 準備

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

```cpp
// 設定連接伺服器所需參數
Proud::CNetConnectionParam cp;

// 您必須輸入與伺服器相同的協定版本。 您可能根本不輸入它。
cp.m_protocolVersion = g_version;
cp.m_closeNoPingPongTcpConnections=false;
cp.m_serverIP = _PNT("localhost");
cp.m_serverPort = 33334;	
```

{% endtab %}

{% tab title="C#" %}

```csharp
using namespace Nettention.Proud;
// NetConnectionParam 命名空間省略

// 設定連接伺服器所需參數
NetConnectionParam cp = new NetConnectionParam();

// 與伺服器協定版本相同，無需輸入
cp.protocolVersion.Set(version);
// server address
cp.serverIP = "localhost";
// server port
cp.serverPort = 33334;
```

{% endtab %}
{% endtabs %}

### 3. 開始

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

```cpp
// 使用上面準備範例中建立的參數。
m_netClinet->Connect(cp);
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 使用上面準備範例中建立的參數。
netClient.Connect(cp);
```

{% endtab %}
{% endtabs %}

#### 客戶端連接到伺服器時發生的事件

> * 當執行 <mark style="color:orange;">Connect</mark> 時，<mark style="color:orange;">OnConnectionRequest</mark> 到達伺服器，您可以在其中拒絕用戶端嘗試連線。
> * 如果您在 <mark style="color:orange;">OnConnectionRequest</mark> 中接受客戶端連接，則所有連接過程都會完成，並且將從客戶端和伺服器接收事件，如下面的程式碼範例所示。

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

```cpp
// 在伺服器上
m_netServer->OnClientJoin = [](CNetClientInfo *clientInfo){
    // 加入客戶端時要執行的邏輯
};

// 來自客戶
m_netClient->OnJoinServerComplete = [&](ErrorInfo *info, const ByteArray &replyFromServer) {
    // 伺服器連線完成後執行的邏輯
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 在伺服器上
netServer.ClientJoinHandler = (clientInfo) => {
    // 加入客戶端時要執行的邏輯
};

// 來自客戶
netClient.JoinServerCompleteHandler = (info, replyFromServer) => {
    // 伺服器連線完成後執行的邏輯
}
```

{% endtab %}
{% endtabs %}

### 4. 解除連接&#x20;

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

```cpp
// 與伺服器斷開連接
m_netClient->Disconnect();
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 與伺服器斷開連接
netClient.Disconnect();
```

{% endtab %}
{% endtabs %}

***

## 活用 <a href="#server-client-usage" id="server-client-usage"></a>

{% content-ref url="/pages/aId6xnt3kOvkFQRH2qj9" %}
[如何使用伺服器](/proudnet.cn/proudnet/using_pn/server_client/using_server.md)
{% endcontent-ref %}

{% content-ref url="/pages/b1BROqkBWO3ti0Lt6oI9" %}
[如何使用客戶端](/proudnet.cn/proudnet/using_pn/server_client/using_client.md)
{% endcontent-ref %}
