안녕하세요 현재 iocp로 게임 서버 구현 중에 있습니다.
제가 현재 고민 중인 부분은 GetQueuedCompletionStatus()에서 64에러(상대방이 소켓 종료)할 때 그 후처리에 관해 문제가 있어 문의 드립니다. 현재 서버에서 30틱 주기로 패킷을 브로드 캐스팅 중입니다. 이때 클라이언트를 종료할 경우, 기존에 send 명령이 들어있던 것들이 전부 false를 리턴하면서 저 스위치의 default 문에 들어가게 되고 Disconnect를 통해 unordered_map<int sid, SeverSession*>로 관리하고 있던 서버 세션을 맵에서 삭제하는 방식으로 진행하고 있습니다. 그런데 이 Disconnect부분에서 delete를 여러 번 수행하다 보니 여기서 더블딜리트 문제로 서버가 터져버리는 현상이 자꾸 발생합니다. 이점을 어떻게 개선하면 좋을까요...? shared_ptr를 사용해보려고 했다가, shared_ptr에 관한 이해가 부족해서 구조 변경을 실패했습니다..
전반적인 iocp 코드는 서버 강의를 들으면서 참고 했습니다.
-Disconnect 부분
void SIocpCore::Disconnect(int32 sid)
{
if (_clients[sid] == nullptr || (_clients.find(sid) == _clients.end())) return;
if(sid >= 0 && _clients[sid]->_myRm != -1) _rmgr->ExitRoom(sid, _clients[sid]->_myRm);
{
std::unique_lock<std::shared_mutex> disconnectLock(_lock);
std::cout << "[" << _clients[sid]->_cid << "] Disconnected" << std::endl;
_cList.erase(_clients[sid]->_cid);
_clients.erase(sid);
}
}
-GetQueuedCompletionStatus 에러 핸들링 부분
bool IocpCore::Processing(uint32_t time_limit) // worker thread 기능 완료된 비동기 통지 명령들을 받아와 적절하게 처리한다.
{
DWORD numOfBytes(0); // 몇 바이트가 전송되었는가?
IocpObject* iocpObject = nullptr; // 일감이 완료된 iocpObject의 종류를 복원하기 위한 IocpObject
IocpEvent* iocpEvent = nullptr; // 일감이 완료된 iocpEvent의 종류(Accept인가?)
BOOL retVal = ::GetQueuedCompletionStatus(_hIocp, OUT & numOfBytes, reinterpret_cast<PULONG_PTR>(&iocpObject), // 하지만 이렇게 iocpObject를 인자로 넘겨주게 되면, 다른 스레드에서 이 오브젝트를 삭제했을 때, 문제가 생길 수도 있다. -->
//애초에 iocpEvent에서 해당 iocp객체들에 관한 정보(해당 이벤트를 호출한 주인 iocp객체들)을 담고 있도록하자.
OUT reinterpret_cast<LPOVERLAPPED*>(&iocpEvent), time_limit);
if (!retVal) // 실패했다면 에러코드 확인
{
int32 errCode = ::WSAGetLastError();
switch (errCode)
{
case WAIT_TIMEOUT: // time_limit이 INFINITE가 아닌 경우 ==> 나중에 다중 접속 시, 접속 시간에 따라 지정 가능
std::cout << "Time Out Plz Check Your Network Condition" << std::endl;
return false;
default:
// TODO : 로그 찍기 errcode는 64가 뜹니다.
{
std::cout << "GetQueuedError " << ::WSAGetLastError() << "\n";
ServerSession* s = static_cast<ServerSession*>(iocpObject);
Disconnect(s->_sid);
}
return false;
}
}
... // 성공 시 Processing