-
IOCP 소켓 입출력 모델을 이용하여 만든 채팅 서버입니다.
-
멀티 스레드로 동작합니다.
-
필요한 조건
-
C++17을 지원하는 Visual studio IDE
-
솔루션에
spdlog라이브러리 설치(프로젝트 - NuGet 패키지 관리 - spdlog v1.0.0 설치)
-
Windows 환경 (IOCP는 MS 환경에서만 지원합니다.)
-
-
빌드 및 실행하기
git clone을 받은 후TCP-socket-chatting-server/ChattingServer_v1.1에 있는 솔루션 파일인ChattingServer.sln을 실행합니다.Debug혹은Release모드로 솔루션을 빌드합니다.TCP-socket-chatting-server/ChattingServer_v1.1/{build mode}로 이동합니다.ChattingServer.exe를 먼저 실행한 다음ChattingClient.exe를 실행합니다.부하 테스트를 하는 경우ChattingServer.exe를 실행한 다음StressServer.exe를 실행합니다.(스트레스 테스트 코드는 리팩토링이 진행중입니다.)
-
초기에 C++98로 작성하였으며, Modern C++로 수정하는 작업을 진행하였습니다.
(Visual Studio는 2005 버전에서 초기 작성하였고, 2019 버전으로 이동)
-
IOCP 서버와 패킷 매니저의 구조는 여기를 참고했습니다.
-
여기서 채팅서버 v1.0 전체 제작 과정을 볼 수 있습니다.
-
클라이언트 단은 추후에 리팩토링을 할 예정입니다.
-
필요한 조건
- C++14을 지원하는 Visual studio IDE
- Windows 환경 (IOCP는 MS 환경에서만 지원합니다.)
-
빌드 및 실행하기
git clone을 받은 후TCP-socket-chatting-server/ChattingServer_v1.0에 있는 솔루션 파일인ChattingServer.sln을 실행합니다.ChattingServer,ChattingClient,StressServer의 세 프로젝트 모두 sdl 검사를 비활성화 해줍니다.릴리즈 모드로 솔루션을 빌드합니다.
TCP-socket-chatting-server/ChattingServer_v1.0/Release로 이동합니다.ChattingServer.exe를 먼저 실행한 다음ChattingClient.exe를 실행합니다.부하 테스트를 하는 경우
ChattingServer.exe를 실행한 다음StressServer.exe를 실행합니다.
AcceptorThreadWorkerThreadPacketThread(WSASendonly)
-
클래스/구조체 상속 관계는 다음과 같습니다.
-
IOCPServer←ChatServerIOCPServer에서는 IOCP를 생성, 초기화하며 리슨 소켓을 활성화하고 스레드를 생성합니다. -
ClientManager←ChatClientManagerClientManager를 상속받은 클래스는ClientInfo를 상속받은 구조체를 이용할 수 있습니다.ClientManager는 클라이언트의 서버 접속을 관리하고,ChatClientManager는 채팅 서버 내의 클라이언트 동작을 관리합니다. -
ClientInfo←UserInfoClientInfo는 클라이언트의 소켓 정보를 관리합니다.UserInfo는 클라이언트의 서버 기능 정보를 관리합니다. (닉네임, 채팅방 정보 등)
-
-
패킷 관련 자료구조는 다음과 같습니다.
-
enum eAction : UINT16 { }클라이언트와 주고받는 패킷에 포함되어 있으며, 어떤 요청인지를 의미합니다.
-
enum eMessage : UINT32 { }클라이언트와 주고받는 패킷의 종류 중
SERVER_MESSAGE_PACKET에 포함되는 값이며, 닉네임 생성 요청 시 클라이언트에게 전송하는 서버의 응답을 의미합니다. -
PACKET_HEADER패킷의 종류와 길이 정보를 가지고 있습니다.
-
SYSTEM_PACKET,ROOM_ENTER_PACKET,CHAT_PACKET,UNICAST_PACKET,SERVER_MESSAGE_PACKETPACKET_HEADER를 상속받으며,Type을 판별한 후 알맞은 패킷에 따라 캐스팅 합니다. -
eSendTypePacketManager에서 사용하며, 해당 패킷을 어떤 방식으로 전송할 지를 가르킵니다. -
PacketInfoPacketManager에서 사용합니다.패킷을 송신한 클라이언트 정보, 패킷의 크기, 전송할 방식, 연결을 종료하는 패킷인지의 여부, 실제 패킷 내용을 가르키는 포인터가 있습니다.
패킷 내용은 패킷 큐에
Enqueue하기 전에 할당하며,Dequeue하고 전송을 완료한 후 해제합니다.
-
-
패킷 매니저에서는
WSAsend()전용 스레드 역할을 하며, 패킷을 담는 큐를 관리합니다.-
ChatServer의WorkerThread에서 패킷을 받으면 해당 패킷Type에 따라 적절한 처리를 하고 패킷 매니저의 큐에 넣습니다. -
패킷 매니저 클래스에서 독립된 스레드로 실행되는
PacketManager::Run()에서는 1번의 큐에서 패킷을 꺼내고, 전송 종류에 따라 알맞는 함수를 호출하여 전송합니다.switch (packetData.SendType) { case SENDTYPE_BROAD: mClientMgr->BroadCast(packetData); break; case SENDTYPE_MULTI: mClientMgr->MultiCast(packetData, packetData.isClose ? CLOSE : SEND); break; case SENDTYPE_UNI: mClientMgr->UniCast(packetData); break; default: break; }
-
-
채팅 서버의 부하를 테스트 하기 위해서 만들었습니다.
-
IOCP 모델을 베이스로 해서 소켓 입출력을 처리하였습니다.
-
여기서 제작 과정을 볼 수 있습니다.
-
스트레스 서버 테스트를 하기 위해서는
ChattingServer프로젝트의ChatClientManager.h의int FindNickname(char*)함수에서 첫 줄에 있는return -1;이 주석처리 되어 있지 않아야 합니다.(닉네임 중복 체크 기능 비활성화)
-
채팅을 주고받을 수 있는 프로그램입니다.
-
닉네임을 생성해야 하며, 로비 / 채팅방 시스템이 있습니다.
-
전체 채팅, 현재 속한 방 채팅, 귓속말 기능이 있습니다.
-
한글을 포함하여 긴 패킷을 보낼 시 문자열 size 때문에 유니코드가 밀려서 이상한 문자가 출력되는 이슈가 있습니다.