4.Start a client

넷텐션

클라이언트는, 서버에 접속하여 메시지를 주고 받거나 P2P 통신을 할 수 있는 콘솔 앱이 되겠습니다.

클라이언트에서 서버에의 접속을 하기 위해, Proud.CNetClient (C::에서는 Nettention.Proud.NetClient) 객체를 만듭시다. 이를 main 함수에서 합시다.

using namespace std;
int main(int argc, char* argv[])
{
    // Network client object.
    shared_ptr<CNetClient> netClient(CNetClient::Create());
    ...
}

서버에 접속하기 위해, Proud.CNetClient.Connect()를 호출합시다. (참고: 10. Connecting to server from client)

CNetConnectionParam cp;
// Protocol version which have to be same to the value at the server.
cp.m_protocolVersion = g_Version;
// server address
cp.m_serverIP = _PNT("localhost");
// server port
cp.m_serverPort = g_ServerPort;
// Starts connection.
// This function returns immediately.
// Meanwhile, connection attempt is process in background
// and the result is notified by OnJoinServerComplete event.
netClient->Connect(cp);

그러나 위의 프로그램처럼 실행하면 서버와의 연결을 시도하자마자 종료하기 때문에 정상적으로 서버 연결의 성공을 간혹 확인하지 못하게 됩니다.

따라서 아래 루틴을 더 이어서 넣읍시다. (참고: 15. Managing Timer loop, RMI and event from client)

// As we have to be notified events and message receivings,
// We call FrameMove function for every short interval.
// If you are developing a game, call FrameMove
// without doing any sleep.
// If you are developing an app, call FrameMove
// in another thread or your timer callback routine.
Proud::Thread workerThread([&](){
    while (keepWorkerThread)
    {
        // Prevent CPU full usage.
        Proud::Sleep(10);

        // process received RMI and events.
        netClient->FrameMove();
    }
});
workerThread.Start();

while (keepWorkerThread)
{
    // get user input
    string userInput;
    cin >> userInput;
}

본 튜토리얼에서는 콘솔 앱을 만드는 중이기 때문에, 게임 프로그램처럼 짧은 시간동안 계속해서 뭔가를 반복 실행하는 것이 없습니다. 따라서 두번째 스레드를 만들었습니다. 게임 프로그램을 만드신다면, 렌더링 루프에서 Proud.CNetClient.FrameMove()를 호출해 주고, 위 예제처럼 스레드를 만들지 않아도 됩니다.

위 Connect 함수 호출은, 즉시 리턴하지만, 아직 서버와의 연결은 안 되어 있습니다. 서버와의 연결 과정이 성공하거나 실패하면 실행되는 이벤트 핸들러를 등록해 줍시다. (8. How to handle events 참고)

// set a routine which is run when the server connection attempt
// is success or failed.
netClient->OnJoinServerComplete = 
    [&](ErrorInfo *info, const ByteArray &replyFromServer)
{
    // as here is running in 2nd thread, lock is needed for console print.
    CriticalSectionLock lock(g_critSec, true);
    if (info->m_errorType == ErrorType_Ok)
    {
        // connection successful.
        printf("Succeed to connect server. Allocated hostID=%d\n",
            netClient->GetLocalHostID());
        isConnected = true;
    }
    else
    {
        // connection failure.
        cout << "Failed to connect server.\n";
    }
};

서버에서는 클라이언트의 연결을 받는 이벤트에서 결과를 출력하게 하겠습니다. 아래처럼 서버에 이벤트 핸들러를 만듭시다. (참고: 8. How to handle events)

// set a routine which is executed when a client is joining.
// clientInfo has the client info including its HostID.
srv->OnClientJoin = [](CNetClientInfo *clientInfo){
    printf("Client %d connected.\n", clientInfo->m_HostID);
};

그리고 나서 클라이언트를 실행 후 서버를 실행하면, 서버와의 연결이 성공했다는 문구와 함께 할당받은 Proud.HostID를 확인할 수 있습니다.