-
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
를 실행합니다.
AcceptorThread
WorkerThread
PacketThread
(WSASend
only)
-
클래스/구조체 상속 관계는 다음과 같습니다.
-
IOCPServer
←ChatServer
IOCPServer
에서는 IOCP를 생성, 초기화하며 리슨 소켓을 활성화하고 스레드를 생성합니다. -
ClientManager
←ChatClientManager
ClientManager
를 상속받은 클래스는ClientInfo
를 상속받은 구조체를 이용할 수 있습니다.ClientManager
는 클라이언트의 서버 접속을 관리하고,ChatClientManager
는 채팅 서버 내의 클라이언트 동작을 관리합니다. -
ClientInfo
←UserInfo
ClientInfo
는 클라이언트의 소켓 정보를 관리합니다.UserInfo
는 클라이언트의 서버 기능 정보를 관리합니다. (닉네임, 채팅방 정보 등)
-
-
패킷 관련 자료구조는 다음과 같습니다.
-
enum eAction : UINT16 { }
클라이언트와 주고받는 패킷에 포함되어 있으며, 어떤 요청인지를 의미합니다.
-
enum eMessage : UINT32 { }
클라이언트와 주고받는 패킷의 종류 중
SERVER_MESSAGE_PACKET
에 포함되는 값이며, 닉네임 생성 요청 시 클라이언트에게 전송하는 서버의 응답을 의미합니다. -
PACKET_HEADER
패킷의 종류와 길이 정보를 가지고 있습니다.
-
SYSTEM_PACKET
,ROOM_ENTER_PACKET
,CHAT_PACKET
,UNICAST_PACKET
,SERVER_MESSAGE_PACKET
PACKET_HEADER
를 상속받으며,Type
을 판별한 후 알맞은 패킷에 따라 캐스팅 합니다. -
eSendType
PacketManager
에서 사용하며, 해당 패킷을 어떤 방식으로 전송할 지를 가르킵니다. -
PacketInfo
PacketManager
에서 사용합니다.패킷을 송신한 클라이언트 정보, 패킷의 크기, 전송할 방식, 연결을 종료하는 패킷인지의 여부, 실제 패킷 내용을 가르키는 포인터가 있습니다.
패킷 내용은 패킷 큐에
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 때문에 유니코드가 밀려서 이상한 문자가 출력되는 이슈가 있습니다.