지난번에는 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

 

+ Recent posts