지난번에는 IOCP를 멀티스레드로 accept, recv, send 하는것 까지 해봤다.
이번엔 Connect와 Disconnect를 구현해보자. 서버 입장에서는 connect는 쓸 일이 아예없지만 이후에 더미클라이언트를 만들어서 테스트하려면 connect가 필요하니 만들도록 하자.
Connect
ConnectEx는 일반 connect와 달리 소켓에 내 로컬 주소를 bind를 해준 다음, 사용해야한다.
주소는 ADDR_ANY, 포트는 0으로 설정하면 OS가 알아서 할당해준다.
연결할 서버의 주소는 ConnectEx 함수의 2번째 인자로 넣는다.
void Session::RegisterConnect()
{
_connectEvent.Init();
_connectEvent.SetOwner(shared_from_this());
if (SocketUtils::BindSocket(_socket, NetAddress()) == false)
{
cerr << "Failed to bind session socket in Session::RegisterConnect()" << endl;
ProcessDisconnect();
return;
}
DWORD BytesSent = 0;
if (false == SocketUtils::ConnectEx(_socket, (const sockaddr*)&_address.GetAddr(),
sizeof(sockaddr), NULL, 0, &BytesSent, &_connectEvent))
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
//TODO 적절한 처리
}
}
}
void Session::ProcessConnect()
{
_connectEvent.Clear();
_isConnected.store(true);
if (ServiceRef service = _service.lock())
{
service->AddSession(static_pointer_cast<Session>(shared_from_this()));
}
RegisterRecv();
}
Disconnect
_isConnect가 true 일때 하나의 스레드만 코드에 접근할 수 있도록 만들었다. (근데 정작 ProcessDisconnect는 여기저기서 써서 의미가 있을지는 모르겠다.)
void Session::RegisterDisconnect()
{
bool expected = true;
if (false == _isConnected.compare_exchange_strong(expected, false))
return;
_disconnectEvent.Init();
_disconnectEvent.SetOwner(shared_from_this());
if (false == SocketUtils::DisconnectEx(_socket, &_disconnectEvent, 0, 0))
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
//TODO 적절한 처리
}
}
}
void Session::ProcessDisconnect()
{
_disconnectEvent.Clear();
_isConnected.store(false);
if (ServiceRef service = _service.lock())
{
service->RemoveSession(static_pointer_cast<Session>(shared_from_this()));
service->broad_cast_test(make_shared<SendBuffer>((BYTE*)"A client has disconnected\n", 28));
}
}
작동 테스트
connect는 아직 더미클라이언트를 만들지 않아 테스트할 수가 없어서 Disconnect만 테스트해봤다.
void WorkerThread(ServiceRef service)
{
while (1)
{
service->GetIocpCore()->Dispatch();
cout << "Worker thread " << LThreadId << " processed an I/O event" << endl;
}
}
int main()
{
ThreadManager threadManager;
ServiceRef service = make_shared<Service>(NetAddress("0.0.0.0", 9000));
service->Start();
for (int i = 0; i < 5; i++)
{
threadManager.Launch([&service]() {
WorkerThread(service);
}
);
}
this_thread::sleep_for(chrono::seconds(5));
service->didconnect_all_test();
}
void Service::didconnect_all_test()
{
lock_guard<mutex> lock(_m);
for (auto it = _sessions.begin(); it != _sessions.end();)
{
(*it)->RegisterDisconnect();
it = _sessions.erase(it);
}
}
main함수에서 서버 시작 5초 뒤에 모든 세션과의 연결을 종료하도록 해봤다. 5초뒤에 접속한 세션들이 모두 종료되고, 세션이 소멸되면 잘 작동하는 것.

성공적으로 5초 뒤에 접속한 3개의 세션이 소멸하고있다. (세션이 4개 생성되는건 acceptEx에 등록할 때 미리 세션을 만들어놓고 등록하기 때문.)
현재까지의 git 버전
Fear: Connect Disconnect · Dodontak/Project_Island_GameServer@7dff917
IocpCore - IocpEvent에서 virtual 함수를 사용할 수 있게 GetQueuedCompletionStatus에서 OVERLAPPED*를 인자로 넣고, static_cast로 변환하여 사용하는것으로 수정. IocpEvent - Clear함수를 virtual 함수로 추가. - 자식클래
github.com
'프로젝트 > Project_Island' 카테고리의 다른 글
| 32. 세션 소멸 안되는 문제 해결 (0) | 2026.04.01 |
|---|---|
| 31. 더미 클라이언트 (0) | 2026.03.31 |
| 29. 멀티스레드 구현, Partial Send 문제 해결 (0) | 2026.03.30 |
| 28. Send, Broadcast 구현 (0) | 2026.03.29 |
| 27. IOCP accept, recv까지 구현 (0) | 2026.03.28 |