# 서버와 클라이언트

## 서버 시작하기

서버를 시작하기 위해 먼저 아래 예시처럼 서버 객체를 얻어야 합니다. 서버를 생성하자마자 클라이언트와의 통신을 하거나 스레드 풀이 바로 생성되진 않으므로 생성된 객체의 <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 포트 방식**](https://docs.proudnet.com/proudnet/usage_pn/tips#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 이후부터는 <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
// 특정 이벤트에 람다로 실행할 로직 등록해서 사용이 가능힙니다.
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";
// 위에서 등록한 port 번호 or 직접 입력합니다.
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 
{
       // Client의 접속이 완료되면 
       // Callback됩니다.
       // CNetClientInfo 객체를 인자로 받습니다. 
       Virtual void OnClientJoin(
           CNetClientInfo *info) 
           OVERRIDE
       {
           // Client의 정보를 받아 
           // 처리합니다.
       }
       // Client의 접속이 해제되면 
       // Callback됩니다.
           Virtual void OnClientLeave(
               CNetClientInfo *info) 
               OVERRIDE
       {
               // Client의 정보를 받아 
               // 처리합니다.
       }
       // 나머지 Event는 생략
}
</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>서버 정지. 모든 Connection을 끊습니다.</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
// 서버 연결에 필요한 parameter 설정
Proud::CNetConnectionParam cp;

// 서버와 같은 protocol 버전을 입력해야 한다. 아예 입력하지 않을 수도 있음.
cp.m_protocolVersion = g_version;
cp.m_closeNoPingPongTcpConnections=false;
cp.m_serverIP = _PNT("localhost");
cp.m_serverPort = 33334;	
```

{% endtab %}

{% tab title="C#" %}

```csharp
using Nettention.Proud;
// NetConnectionParam 네임스페이스 생략

// 서버 연결에 필요한 parameter 설정
NetConnectionParam cp = new NetConnectionParam();

// 서버와 동일한 protocol version, 입력하지 않아도 됨
cp.protocolVersion.Set(version);
// server address
cp.serverIP = "localhost";
// server port
cp.serverPort = 33334;
```

{% endtab %}
{% endtabs %}

### 3. 시작

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

```cpp
// 위 준비 예시에서 만들어 둔 parameter를 사용
m_netClinet->Connect(cp);
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 위 준비 예시에서 만들어 둔 parameter를 사용
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){
    // Client Join 시 실행할 로직
};

// 클라이언트에서
m_netClient->OnJoinServerComplete = [&](ErrorInfo *info, const ByteArray &replyFromServer) {
    // Server 연결 완료 시 실행할 로직
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// 서버에서
netServer.ClientJoinHandler = (clientInfo) => {
    // Client Join 시 실행할 로직
};

// 클라이언트에서
netClient.JoinServerCompleteHandler = (info, replyFromServer) => {
    // Server 연결 완료 시 실행할 로직
}
```

{% 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="server\_client/using\_server" %}
[using\_server](https://docs.proudnet.com/proudnet/proudnet/using_pn/server_client/using_server)
{% endcontent-ref %}

{% content-ref url="server\_client/using\_client" %}
[using\_client](https://docs.proudnet.com/proudnet/proudnet/using_pn/server_client/using_client)
{% endcontent-ref %}
