작성
·
63
·
수정됨
0
안녕하세요.
거의 하루종일 보고 있는 문젠데요.
클라이언트에서 에코해줄 때 강사님은 OnRecvCompleted() 함수 안에서 인자로 받은 length가 아니라 sizeof send_buffer를 사용하고 계셔서 오류가 발생 안 했어요.
인자로 받는 length를 해주면 프로그램 구동하고 나서 얼마 안돼서 주고받는 데이터가 영원히 증가합니다. 물론 ASSERT_CRASH가 있기 때문에 메모리 할당해주는 부분에서 결국 멈추긴 합니다.
정확한 이유를 못 찾겠네요. 이리저리 중단점 찍어보는데, 한 가지 확실한 건 서버에서 WSASend() 해주고 클라에서 받을 때 갑자기 데이터 사이즈가 튑니다. 더미 클라에서 세션을 하나만 넣어줘도 재현돼요.
이럴 때 비동기 + 멀티스레드는 정말 헬이네요. ProcessSend()로 들어오는 sendBytes가 갑자기 26, 52..이렇게 되어버립니다. GSendBufferManager에서 반환하는 메모리 위치는 문제가 없어요. 어느 순간 데이터를 미친 듯이 이어 붙입니다.
위 이미지처럼 하나씩만 오고 가야 하는데(Hell, World!)
어느 순간 아래 이미지처럼 확 늘어납니다.
계속 누적돼요. 강사님 코드 거의 그대로입니다.
WSASend() 동작이 중첩됐나까지 의심하게 되네요. 그러나 송신 큐 부분에 락 걸고 이후 코드는 스택 변수라 강사님과 동일하게 진행됩니다.
void Session::RegisterSend()
{
_send_event.Init();
_send_event.owner = shared_from_this();
// 보낼 데이터를 send_event에 등록
// 레퍼런스 카운트 유지를 위해 SendEvent의 멤버변수를 이용한다
{
WRITE_LOCK;
while (false == _send_queue.empty())
{
SharedSendBuffer send_buffer =
_send_queue.front();
_send_queue.pop();
_send_event.send_buffers.push_back(send_buffer);
}
}
xvector<WSABUF> wsabufs;
wsabufs.reserve(_send_event.send_buffers.size());
for (SharedSendBuffer send_buffer : _send_event.send_buffers)
{
WSABUF wsabuf;
wsabuf.buf = reinterpret_cast<char*>(send_buffer->Buffer());
wsabuf.len = static_cast<LONG>(send_buffer->WriteSize());
printf("wsabuf len: %d\n", wsabuf.len);
wsabufs.push_back(wsabuf);
}
DWORD send_bytes = 0;
int32 result =
WSASend(_socket, wsabufs.data(), static_cast<DWORD>(wsabufs.size()), &send_bytes, 0, &_send_event, nullptr);
int32 error = WSAGetLastError();
if (SOCKET_ERROR == result && error != WSA_IO_PENDING)
{
HandleError(error);
// release ref
_send_event.owner = nullptr;
_send_event.send_buffers.clear();
_send_registered.store(false);
return;
}
}
정상적인 에코 서버라면 주고 받는 데이터의 크기가 계속 일정해야 한다고 생각해서 테스트한 건데 고통의 시간을 겪고 있습니다..
참고로 GameSession에서 OnRecv 재정의할 때 1회만 브로드캐스트 해주고 있습니다.
int32 GameSession::OnRecvCompleted(BYTE* buffer, int32 length)
{
// temp echo
printf("GameSession OnRecvCompleted len: %d\n", length);
SharedSendBuffer send_buffer = g_send_buffer_manager->Open(4096);
memcpy(send_buffer->Buffer(), buffer, length);
send_buffer->Close(length);
g_session_manager.Broadcast(send_buffer);
return length;
}
답변 2
0
자문자답합니다.
더미 클라이언트에서 2인 이상의 세션을 넣었을 때만 해당 증상이 일어나는 걸 보고 고민고민하다가 발견했습니다. 아무리 봐도 코드는 문제 없었거든요.
이전 코드에서는 브로드캐스팅을 하지 않고 각각의 세션에게만 에코를 해주니까 클라이언트가 수신할 때 서버가 송신해준 것만 딱딱 받았어요.
여러 세션이 브로드캐스팅을 해주면, 서버가 여러 번 송신하는 데이터들이 세션에서 한 번에 받는 일들이 생기고 이게 누적되면서 눈덩이처럼 주고받는 데이터가 불어나더라고요.
이상입니다.
0
안녕하세요, 인프런 AI 인턴입니다. literate_t님, 비동기 네트워킹과 멀티스레드 환경에서 발생한 오류로 인해 어려움을 겪고 계신 것 같습니다. 이 문제는 특히 WSASend와 관련된 동작에서 빈번히 발생할 수 있는 복잡한 상황입니다.
귀하의 설명을 바탕으로 몇 가지 사항을 고려할 수 있습니다:
sendEvent 객체 관리: sendEvent
객체가 잘못된 시점에서 해제되면 예상치 못한 동작이 발생할 수 있습니다. 이 객체의 수명 주기를 관리할 때, 적절한 타이밍에 객체가 해제(Release)되고 다시 필요 시 새로 할당될 수 있도록 확인해야 합니다.
길이 확인: 코드를 검토한 결과, WSASend
가 데이터 길이를 처리할 때 sizeof(send_buffer)
가 사용되어야 하며, 클라이언트에서 의도한 길이로 데이터가 전송되지 않는 것 같습니다. 이 부분을 좀 더 면밀히 살펴보실 필요가 있습니다.
큐에서 데이터 이어붙이기: 데이터가 잘못 이어 붙여지는 경우, _send_queue
에 잘못된 상태에서 데이터가 쌓이다가 중복 전송되는 경우를 확인해 보셔야 할 것 같습니다.
비슷한 주제의 토론에서 유사한 문제를 겪은 다른 사용자들의 답변이 도움이 될 수 있습니다:
sendEvent 객체가 해제되어 버립니다.
설명: 클라이언트와 서버 간의 송수신 문제를 다룬 질문으로, 메모리 관리가 주요 논점입니다. refCount 관련해 체크하라는 답변이 있습니다.
WSASend 관련 처리 문의
설명: WSASend
의 부분 성공 처리에 관한 질문으로, 실제 전송이 실패할 때 안전하게 처리하는 방법을 다루고 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.