클라이언트는, 서버에 접속하여 메시지를 주고 받거나 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를 확인할 수 있습니다.