지난번에는 더미클라이언트를 강제종료했을 때 session이 소멸되지 않고 남아있는 문제를 해결했다.
테스트를 하다가 다른 문제도 발견했다.
문제
지금 채팅서버는 받은 데이터를 그대로 모든 클라이언트에게 다시 보내준다.
그러면 2명의 클라가 Hello 를 보내면 서버는 2명의 클라에게 2번씩 Hello를 보내니 더미클라 입장에서는 각 클라당 2번, 총 4개의 Hello를 recv 해야한다.

그런데 보내는건 2번만 보내는데 받을때는 6번을 받는다.
원인 파악
Recv 문제인가?
처음에 확인한건 더미클라이언트 측 Session의 _recvBuffer에 서버가 이전에 보낸 데이터가 출력 이후에도 남아있어서 그런가? 하고 확인해봤다.
Session::ProcessRecv
_recvBuffer.OnWrite(numOfBytes);
SendBufferRef sendBuffer = make_shared<SendBuffer>(_recvBuffer.ReadPos(), numOfBytes);
_recvBuffer.OnRead(numOfBytes);
_recvBuffer.Clean();
if (dynamic_pointer_cast<ClientService>(_service.lock()))
{//더미 클라이언트 서비스라면.
string str((char*)sendBuffer->GetBuffer(), sendBuffer->GetDataLen());
Utils::LockPrint("recv from server : ", str);
RegisterRecv();
return;
}
recv 완료통지 이후에 실행되는 ProcessRecv에서 recvBuffer의 내부 포인터를 관리함수를 호출하는데,
OnWrite는 "데이터가 이만큼 쓰여졌음" 을 기록하고
OnRead는 "버퍼에서 데이터 이만큼 사용했음" 을 기록한다.
여기서 받은 데이터 길이와 recvBuffer에 남아있는 길이를 비교했는데, 다르면 이전 recv버퍼가 제대로 비워지지 않았다는 말이니까 이걸 확인하면 ProcessRecv 쪽 문제인지 확인이 가능하다.
_recvBuffer.OnWrite(numOfBytes);
SendBufferRef sendBuffer = make_shared<SendBuffer>(_recvBuffer.ReadPos(), numOfBytes);
Utils::LockPrint(_recvBuffer.DataSize(), ' ', numOfBytes);
_recvBuffer.OnRead(numOfBytes);
_recvBuffer.Clean();
recvBuffer에 "데이터 쓰여졌음" 을 기록한 뒤에 datasize와 numOfBytes를 출력하게 해봤다.

비교해봤는데 같다. 그러면 실제로 서버가 클라에게 너무 많이 보냈다는 말. recv쪽 문제는 아니고 아무래도 sendBuffer의 관리가 잘못된 것 같다.
Send 문제인가?
ProcessSend에서 문제를 확인했다.
정상적으로 Send가 이루어졌을 때 SendEvent를 Clear하지 않아서 생긴 문제... 생각보다 별거 아니었다.
해결
void Session::ProcessSend(int32 numOfBytes)
{
if (numOfBytes == 0)
{
_sendEvent.Clear();
RegisterDisconnect();
return;
}
// 보낸 바이트 수 < 보내려 했던 바이트 일 경우 처리.
if (numOfBytes < _sendEvent.GetWantSendBytes())
{
uint32 sendedBytes = numOfBytes;
deque<SendBufferRef>& sendBuffers = _sendEvent.GetSendBuffers();
while (!sendBuffers.empty())
{
if (sendedBytes >= sendBuffers.front()->GetDataLen())
{
sendedBytes -= sendBuffers.front()->GetDataLen();
sendBuffers.pop_front();
}
else
{
BYTE* pos = sendBuffers.front()->GetPosPtr(sendedBytes);
int32 dataLen = sendBuffers.front()->GetDataLen() - sendedBytes;
SendBufferRef newone = make_shared<SendBuffer>(pos, dataLen);
sendBuffers.pop_front();
_sendEvent.PushFront(newone);
RegisterSend();
return;
}
}
return;
} else {
_sendEvent.Clear();
{
lock_guard<mutex> lock(_m);
if (_sendBuffers.empty())
{
_sendRegistered.store(false);
return;
}
}
RegisterSend();
}
}
성공적으로 모든 데이터를 send했을 때 _sendEvent를 clear하는걸로 쉽게 해결됐다.
그리고 이걸 넣기 전에는 send작업을 한 다음에 터미널을 닫으면 또 sendEvent의 owner가 nullptr로 set되지 않아서 순환참조 문제로 session이 소멸하지 않는 문제가 있었는데, 이걸 넣으면서 그 문제도 해결됐다.

이제 클라이언트 2명이 서버로 메시지를 1개씩 보내면, 정상적으로 4개 돌아온다.
(A가 보낸 메시지를 A B에게 send, B가 보낸 메시지를 A B에게 send해서 4개가 맞음)
새로운 문제(?)

여전히 종종 메시지가 겹쳐서 오는데, 이건 정확히 말하면 문제는 아니고 send작업 시 session의 sendbuffers에 모아서 한번에 보내기때문에 어쩌다가 메시지가 쌓이면 모인 메시지가 한번에 보내지는 정상적인 상황이다.
단, 패킷 헤더를 만들어 어디서부터 어디까지가 하나의 메시지인지는 구분해야 할 필요가 있다. 그러니까 다음에는 패킷헤더를 만들어보자.
현재까지의 git 버전
Fix: ProcessSend에서 Clear 안하는 문제 수정 · Dodontak/Project_Island_GameServer@ac6c6f0
Session - ProcessSend에서 정상작동 시 SendEvent.Clear를 해줘야했는데, 안하고있어서 send가 중복으로 이루어지는 문제가 있었다. 해결 완료.
github.com
'프로젝트 > Project_Island' 카테고리의 다른 글
| 35. Protobuf 추가하기 (0) | 2026.04.02 |
|---|---|
| 34. PacketHeader, PacketSession 추가 (0) | 2026.04.02 |
| 32. 세션 소멸 안되는 문제 해결 (0) | 2026.04.01 |
| 31. 더미 클라이언트 (0) | 2026.03.31 |
| 30. Connect, Disconnect 구현 (0) | 2026.03.31 |