계획

아래와 같이 Update() 로직을 구현하면 하나의 Frame에서 recv/send 동작을 꼬이지 않게 분리하면서 World의 데이터를 업데이트할 수 있을 것 같다. 이를 위해선 데이터를 쓰는 시점과 읽는 시점을 분리하기 위한 이벤트큐가 필요하다. NetworkManager는 소켓이 우선 OS 자체의 버퍼에 먼저 쌓이기 때문에 지금은 그냥 둔다. NetworkManager로부터 얻어낸 변경사항을 통해 World를 업데이트하기 위한 이벤트 큐를 우선 구현한다.

Server_Update_Frame.drawio.png

진행 내용

serverMain 코드의 게임 루프 코드를 아래와 같이 구성했다.

networkInstance에서는 서버와 연결된 소켓을 관리하며 패킷을 주고 받고자 한다.

worldInstance는 GameObjects를 멤버로 가지고 world를 지속 업데이트 해줄 예정이다.

while (engine.isRunning) {

	networkInstance.ReceivePackets();
	worldInstance.Update();
	networkInstance.SendPackets();
}

client에서 msg의 사이즈보다 긴 입력을 받아버려서 에러 발생. 긴 입력이 발생한다면 입력의 뒷쪽을 잘라주어야 하겠다.

image.png

client에서 socket 연결 중 비정상 종료시, server에서 해당 clientSocket으로 send/recv 하지 않도록 closesocket(clientSocket) 해주어야 한다. 이를 위해서 clientSocket의 컨테이너를 list로 구현하고, recv 로직 중간에 closesocket이 함께 동작한다. 분리를 하고 싶지만 우선 이렇게 동작하도록 둔다.

list를 순회하는 코드와 소켓을 제거하는 코드는 분리해도 될 것 같았다. clientSocketItersToErase vector에 close할 소켓만 담아두고, 일괄 삭제하는 것으로 한다. list니까 $O(1)$ 시간 복잡도를 보장하기에 성능면에서도 좋은 코드이다.

std::list<SOCKET> m_clientSockets 멤버는 멤버를 포함하는 class의 생성자에서 초기화를 하지 않아도 생성자 코드에서 warning을 알려주지 않는다. 생성자가 이미 호출되는 것 같다.

→ 다음 페이지에서 검증 완료 멤버 변수가 생성자를 갖는 class일 때

멤버의 생성자가 먼저 호출된다.

WSACleanup();
cout << "WSACleanup" << endl;

// accept로 대기 중인 thread를 종료하고 싶은데 어떻게 해야 할까..
// => WSACleanup을 하면 accept의 block이 풀리는 듯하다. 그리고 10004 에러 발생.
acceptSocketThread->join();