묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
OnDisconnected() 부근에서 _players.clear() 의문점이 있습니다.
06:05 부근 질문입니다.클라이언트가 캐릭터 선택과 동시에 연결이 종료될 때 서버가 혼잡하다면, Handle_C_ENTER_GAME()과 OnDisconnect()가 동시에 일어날 수 있다고 생각되는데요.. 그렇다면 멀티 스레드에서 _players에 동시 접근하는 일이 발생하지 않나요?그렇게 된다면, _players.clear() 이후 _players[index] 접근하는 문제로 서버가 튕길 거라고 생각했습니다.지금까지는 그러한 오류가 터진 적이 없었는데, 테스트가 부족했던 것인지 아니면 잘못 생각하고 있는 것인지 알고 싶습니다.
-
해결됨[켠김에 출시까지] 유니티 캐주얼 모바일 MMORPG (M2)
CPU를 과도하게 점유하면서 멈추는 현상
안녕하세요 쓰레드 배분 강의 소스 코드부터 현재 강의까지, 서버가 정상적으로 동작하지 않는 현상이 있어 확인 요청드립니다.DBThreadJob 메서드에서 _executeQueue가 비어 있을 때 루프가 무한히 반복되면서 CPU를 과도하게 점유하는 문제가 있는 것 같습니다. 저는 macOS에서 서버를 실행 중인데, Windows와의 CPU 스케줄링 차이로 인해 발생하는 문제로 추측됩니다. 확인해주시면 감사하겠습니다!
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
TCP 통신 데이터경계
안녕하세요.TCP 통신에서 전송 순서와 패킷 손실을 보장해준다고 할 때,보내는 데이터가 서버의 수신 버퍼 크기를 초과할 경우, 이전에 받은 데이터와 다음에 받는 데이터를 합쳐서 원하는 형태로 처리하게 되는 걸까요?예를 들어, 서버의 수신 버퍼가 5바이트일 때,1차 수신 데이터가 "L5AAA"이고2차 수신 데이터가 "AA"라면,"L5"는 데이터의 길이가 5라는 뜻이면, 이 경우 데이터가 부족하면 다음 수신 데이터를 기다려 "L5AAAAA" 형태로 합쳐 처리하는게 맞을까요?
-
미해결[켠김에 출시까지] 유니티 방치형 키우기 게임 (M1 + T2)
첫 강의 manager 가 재생이 안됩니다.
첫 강의 manager 가 재생이 안됩니다.
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
환경설정 20:20초 빌드 시
빌드 시 링크 에러 2개가 나옵니다.강의 따라서 잘 타이핑 하고 따라왔는데..이 두개의 오류가 나오면서 빌드 실패를 합니다.왜 그런걸까요?
-
미해결[켠김에 출시까지] 유니티 방치형 키우기 게임 (M1 + T2)
T2는 어떻게된걸까요?
T2가 지금 멈춰있는것같은데..어떻게 진행 되는걸까요?ㅜㅜ
-
미해결[켠김에 출시까지] 유니티 방치형 키우기 게임 (M1 + T2)
9주차 강의 'Stage 분할' 이 로딩만 뜨고 재생되지 않습니다
전후 강의 동영상은 정상적으로 재생이 되는데, 이 강의는 로딩만 계속되고 화면이 나오지 않네요. 혹시 제가 뭐 세팅을 변경해야 하는 것이 있으면 알려주시기 바랍니다.맥/윈도우 크롬 브라우저를 이용해서 수강 중입니다.가능하다면 빠른 해결이 되었으면 합니다.
-
미해결[켠김에 출시까지] 유니티 방치형 키우기 게임 (M1 + T2)
강의 완료 시기
강의 완료시기가 언제쯤인지 알 수 있을까요?
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
다른 스레드가 write하고 있을 때 readlock 허용하나요?
ReadLock 코드uint32 expected = (_lockFlag.load() & READ_THREAD_MASK); if (_lockFlag.compare_exchange_strong(OUT expected, expected + 1)) return;WriteLock 코드const uint32 desired = ((LThreadId << 16) & WRITE_THREAD_MASK); ... uint32 expected = EMPTY_FLAG; if (_lockFlag.compare_exchange_strong(OUT expected, desired)) { _writeCount++; return; }Write할 때 lockFlag를 LThreadId << 16로 저장하기 때문에 첫 비트 ~ 15비트까지는 0으로 초기화가 될것입니다.ReadLock에서는 Read용 Mask를 쓰는데 그럼 Write에서 초기화해준 lockFlag를 볼 수 없어서 항상 참값이 나와 expected + 1로 수정되는거 아닌가요?? 다른 스레드가 Write하고 있을 때 Read를 허용하는것인지 궁금합니다.
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Protobuf 사용시 Lnk2001 에러 두줄
이 두줄을 이틀째 해결 못하고있습니다.3.17.0 ~ 3.19.x 버전은 빌드 O5.27.0 ~ 5.28.x 버전은 빌드 X 최신 버전 및 그 근처 버전들은 전부 마지막에 저 두 에러가 떠서 결국 잠시 내려놓고 다른 방법을 찾아봤습니다.공식문서를 참고해 vcpkg에서 가장 최신버전을 설치해보니 4.25.1로 빌드되고 니 프로젝트가 문제 없이 빌드 되는것 확인했습니다. 최신버전 한 번 사용해보겠다고 이것저것 시도한건 다 실패한건 아쉽지만 그래도 안전하게 사용하가능한 여러 버전을 알게 됐네요.vcpkg에서 설치하면 .lib이나 google을 안넣어도 자동으로 적용되는데 문제 없을까요?아니면 안전하게 설치되는 버전을 파악했으니 삭제하고Cmake로 해당 버전을 다시 빌드할까요?그리고 최신 버전은 꼭 사용할 필요 없는 거겠죠?
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
오류 원인을 찾기가 어렵네요.
안녕하세요.거의 하루종일 보고 있는 문젠데요.클라이언트에서 에코해줄 때 강사님은 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; }
-
해결됨[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Session #1에서 owner를 이용한 레퍼런스 관련한 부분 질문입니다.
몇 년 전에 강의를 들었을 때 느꼈던 거랑 요새 다시 들으면서 느끼는 게 많이 다르네요. 새삼 코드 퀄리티가 너무 좋습니다. 감사합니다.레퍼런스 카운트 관련해서 궁금한 점이 있습니다. Session 객체는 Service의 sessions에서 관리하는 한, 비동기 IO 작업 중에 메모리가 해제될 일은 없을 거라고 생각되는데요.그럼에도 불구하고 이벤트 객체에 owner 객체를 둬서 레퍼런스 카운트를 1을 올려놓은 채로 작업을 진행하는 건 정말 혹시 모를 상황에 대한 대비일까요?연결이 끊어져야 sessions에서 세션 객체를 꺼내니까(~Session 소멸자 호출 확인) 웬만한 상황에서는 비동기 IO 중에 세션 객체가 날아갈 일은 없을 것 같단 생각이 들었습니다. 만약 그 상황에 대한 대비라면 지금 구조에서는 ProcessRecv가 시작하자마 nullptr으로 밀어버리는 것도 RegisterRecv()를 호출하기 직전으로 옮겨야 하지 않나 하는 생각도 들었습니다. 고견을 듣고 싶습니다!감사합니다!
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
template function과 inline 키워드 관련 문법질문
클래스 몸체(body)밖에 템플릿함수의 구현부를 작성할 때는 inline키워드를 사용하고몸채내부에는 사용하지 않고 있어서 궁굼해서 해당 내용을 찾아봤습니다. inline 키워드를 사용하는 이유는 ORD(one definition rule) 그니깐 중복정의를 막기위해서 사용합니다. 2. 쓸 때 없는 스택생성을 막아 오버헤드 최적화하지만 굳이 inline 키워드를 붙여주지 않더라도 컴파일러가 알아서 처리해주고inline키워드를 붙이더라도 컴파일러가 상황에 따라서는 적용시키지도 않기 때문에 inline 키워드는 쓰지 않아도 된다고 결과를 얻었는데 맞는걸까요?
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
서버 배포 관련해서 질문드립니다.
지금 올라와 있는 서버 강의를 듣던 중에 배포 관련해서 궁금하여 질문 남깁니다.c++게임서버를 aws에 배포하는 과정 내용의 강의를 올리실 생각이 있으신가요?배포하는 과정이 궁금합니다.
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
클라이언트간 p2p 통신으로 싱크?를 맞추는걸 공부해보고 싶습니다
안녕하세요 루키스님서버<-> 클라이언트 통신이 아닌, 클라이언트간 p2p 통신으로 싱크?를 맞추는걸 공부해보고 싶습니다.혹시 이런 내용의 강의를 준비중이시거나 추천해주실만한 책이 있을까요?
-
해결됨[켠김에 출시까지] 유니티 캐주얼 모바일 MMORPG (M2)
protobuf 자동화 관련되서 질문 드립니다
protocol.proto를 수정한 후 패킷 자동화를 돌렸는데 Protocol.cs가 수정되지 않았습니다.혹시 GameServerPacketManager.cs 가 먼저 수정되고 Protocol.cs가 이후에 수정되면서 에러가 발생하여 문제가 생기는 걸까요?
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part9: MMO 컨텐츠 구현 (DB연동 + 대형 구조 + 라이브 준비)
로그인 정보 암호화는 어떻게 하나요?
클라에서 서버로 로그인 정보같은 민감한 정보를 보낼 때 암호화를 해야할거같은데 암호화 방법을 모르겠습니다. 대칭, 비대칭키를 사용해서 암호화를 한다면 키 관리를 어떻게 해야할지 모르겠어요SSL/TLS를 쓰는곳도 있다고 들었습니다. 어떤식으로 동작하는지 궁금합니다!
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
TypeCast에서 다루는 템플릿 흑마법 정말 잘 들었습니다!
안녕하세요!빨간책(moden C++ design)이 온라인에 검색해보니까 해외 직구로 살 수 있긴 하더라고요.지금 이직 준비 단계에선는 보기 어려울 거 같아 일을 시작하면 읽고 싶단 생각이 듭니다.괜찮을까요?
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
세션 레퍼런스 카운트 관련 질문입니다.
void Session::ProcessConnect() { _connectEvent.owner = nullptr; // (1) _connected.store(true); // (2) // 세션 등록 GetService()->AddSession(GetSessionRef()); // 컨텐츠 코드에서 재정의 OnConnected(); // 수신 등록 RegisterRecv(); }5장 Session#3 강의를 듣고 따라해보다가 클라이언트 프로그램이 위 함수를 실행하다가 터지는 문제가 발생했습니다. 디버깅을 해보니(1)에서 _connectEvent.owner의 strong ref가 1에서 0으로 줄어들면서 세션(this)이 삭제되고(2)에서 this를 참조하면서 문제가 생긴 것이었습니다. bool IocpCore::Dispatch(uint32 timeoutMs) { DWORD numOfBytes = 0; ULONG_PTR key = 0; IocpEvent* iocpEvent = nullptr; if (::GetQueuedCompletionStatus(_iocpHandle, OUT &numOfBytes, OUT &key, OUT reinterpret_cast<LPOVERLAPPED*>(&iocpEvent), timeoutMs)) { // (A): 강사님 코드 // strong ref: 1 IocpObjectRef iocpObject = iocpEvent->owner; // strong ref: 2 iocpObject->Dispatch(iocpEvent, numOfBytes); // (B): 제 코드 // strong ref: 1 //iocpEvent->owner->Dispatch(iocpEvent, numOfBytes); } else { int32 errCode = ::WSAGetLastError(); switch (errCode) { case WAIT_TIMEOUT: return false; default: // TODO : 로그 찍기 IocpObjectRef iocpObject = iocpEvent->owner; iocpObject->Dispatch(iocpEvent, numOfBytes); break; } } return true; }강사님 코드와 제 코드에서 어떤 부분이 다른지 확인해 본 결과 위와 같은 차이가 있었습니다. 강사님은 지역 변수로 스마트 포인터를 하나 만들어서 레퍼런스 카운트가 2인 상태로 세션->Dispatch 함수를 호출하는 반면, 저는 레퍼런스 카운트가 1인 상태로 세션->Dispatch 함수를 호출하는 차이가 있었습니다. [질문 1] 혹시 강사님은 코드를 짜실 때 (B)와 같이 코드를 짜면 문제가 생긴다는 걸 인지하고 (A)와 같이 짜신 걸까요? 레퍼런스 카운트를 어느 정도까지 고려하면서 프로그래밍을 하는 것이 좋은지 궁금합니다. [질문 2] 아래와 같이 코드를 수정해서 문제를 해결해도 별 문제가 없을까요?// 원본 코드 void Session::ProcessConnect() { // RELEASE_REF _connectEvent.owner = nullptr; _connected.store(true); // 세션 등록 GetService()->AddSession(GetSessionRef()); // 컨텐츠 코드에서 재정의 OnConnected(); // 수신 등록 RegisterRecv(); }// 수정된 코드 void Session::ProcessConnect() { _connected.store(true); // 세션 등록 GetService()->AddSession(GetSessionRef()); // RELEASE_REF _connectEvent.owner = nullptr; // 컨텐츠 코드에서 재정의 OnConnected(); // 수신 등록 RegisterRecv(); }
-
미해결[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
ObjectPool 강의에서 메모리 오염에 관한 질문입니다!
안녕하세요!메모리 풀의 단점 중 하나가 메모리가 오염됐을 때 계속 재사용되다가 시간이 지나서 문제가 되니까 원인 찾기가 어렵다고 말씀하셨는데요.그게 정확히 어떤 식의 오염인지 감이 안 와서 잘 와닿지 않았습니다. 요구하는 사이즈와 같거나 큰 크기의 메모리를 할당받아서 사용하고 반납하는데, 어떤 오염이 발생할 수 있는 건지 알고 싶습니다! 심지어 다른 애가 그 오염된 메모리를 사용하는 게 문제라고 하셨는데 그 오염된 상태가 어째서 계속 유지될 수 있는지 궁금합니다.