# 伺服器和客戶端

## 啟動伺服器

要啟動伺服器，首先需要取得伺服器對象，如下例所示。 伺服器建立後，不會立即建立與客戶端或執行緒池的通信，因此必須透過在建立的物件上呼叫 <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 %}


---

# 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.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.
