월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
SocketAsyncEventArgs 가 연속된 패킷을 모으는 역할을 하나요?
[테스트1]아래 처럼 클라이언트에서 Send를 5번 하는데for (int i = 0; i < 5; i++) { byte[] sendBuffer = Encoding.UTF8.GetBytes($"Hello World! {i} "); int sendByte = socket.Send(sendBuffer); } 강의 결과 화면처럼 서버에서는 5번의 Send 패킷을 모았다가 출력하는 모습을 볼 수 있습니다. [테스트2]아래에서도 마찬가지로 Send를 5번 하는데,1초 딜레이를 주고 실행했습니다.for (int i = 0; i < 5; i++) { byte[] sendBuffer = Encoding.UTF8.GetBytes($"{i} "); int sendByte = socket.Send(sendBuffer); Thread.Sleep(1000); } 이때는 서버에서 패킷을 모을 시간이 없었던건지,Send 패킷을 안모으고 출력하는 모습을 볼 수 있습니다. [질문]서버에서 패킷을 모으는 역할을 하는 것이 무엇인가요?SocketAsyncEventArgs 인가요?그리고 패킷을 모으는 기준이 무엇인지 궁금합니다.시간인가요? 아니면 끊임 없이 연속적으로 보내지는 패킷인가요? 아니면 다른 무엇인가요?SocketAsyncEventArgs recvArgs = new SocketAsyncEventArgs(); recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvCompleted); recvArgs.SetBuffer(new byte[1024], 0, 1024);
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
onAcceptHandle 추가 위치 질문 드립니다.
_listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _onAcceptHandler += onAcceptHandler; _listenSocket.Bind(endPoint); _listenSocket.Listen(10)위 코드에서 new Socket() 한 뒤에_onAcceptHandler += onAcceptHandler; 로 핸들러를 추가 했는데,아래 코드처럼 Listen() 뒤에 추가 해도 문제 없을까요?_listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _listenSocket.Bind(endPoint); _listenSocket.Listen(10); _onAcceptHandler += onAcceptHandler;
- 해결됨[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
byte[] -> ArraySegment 변환 중 생략 된 부분
더미클라와 서버의 Program.cs에서byte가 arraysegment 부분으로 변환되는게 생략되었습니다.어려운 작업은 아니지만, 뒤에 듣는사람 참고하라고 올립니다. 아닌가.. 내가 잘못한 부분이 있었네 public override void OnConnected(EndPoint endPoint){Console.WriteLine($"OnConnected bytes : {endPoint}");byte[] tempBuff = Encoding.UTF8.GetBytes("Welcome to MMORPG Server!");ArraySegment<byte> sendBuff = new ArraySegment<byte>(tempBuff);Send(sendBuff);Thread.Sleep(1000);Disconnect();} public override void OnConnected(EndPoint endPoint){Console.WriteLine($"OnConnected bytes : {endPoint}");//데이터를 보낸다for (int i = 0; i < 5; i++){byte[] tempBuff = Encoding.UTF8.GetBytes($"Hello World {i}");ArraySegment<byte> sendBuff = new ArraySegment<byte>(tempBuff);Send(sendBuff);}}
- 해결됨[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
VS 툴팁 이전/다음 단축키 질문드립니다.
루키스님 안녕하세요?VS 단축키가 궁금해서 질문 올립니다.강의 10:56초툴팁 확인할 때 다음/이전 단축키가 무엇인가요?tab은 될때 있고 안될 때 있는 것 같아서요위아래 화살표는 자동완성 따라가서 잘 안되는 것 같아요.
- 해결됨[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
랜덤메타(Sleep(1), Sleep(0), Yield())는 컨텍스트 스위칭이 없나요?
루키스님 안녕하세요?SpinLock, sleep/Yield, event, Mutex까지 잘 들었습니다. 제가 이해하기로는 SpinLock은 계속 돌기 때문에 컨텍스트 스위칭이 없어 (여기서의 예에서만) 빠르게 _num = 0 을 출력하고, event와 Mutex는 커널 단까지 컨텍스트 스위칭이 일어나 반복문이 조금만 많아도 _num = 0을 출력하는데 시간이 걸리는걸 볼 수 있었습니다. 그런데 궁금한건, 랜덤메타인 Sleep와 Yield도 쉬는 동안 커널 단까지 컨텍스트 스위칭이 일어날 것 같은데(쉬는 동안 다른 스레드에 메모리 <-> 레지스터 가 왔다 갔다 하므로) 여기서의 예에서는 _num이 빠르게 출력 되었습니다. 그래서 빠르게 출력된 이유가 랜덤메타는 컨텍스트 스위칭이 없어서인지, 아니면 컨텍스트 스위칭이 일어나는게 맞지만 여기서의 예에서만(간단한 예제이므로) 빠르게 _num= 0이 출력된 건지 궁금합니다.
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
참고 서적
면접 가기전에 서버 강의를 다시 보고 있는데자동화 방식에 대한 설계 같은 부분이 정말 대단하다고 느껴지네요...추후에 프로토콜 버퍼로 바꾸긴 하지만 이렇게 자동화해서 PDL에 패킷만 추가해서 EXE 실행 후 서버, 클라 세션에 각각 코드가 들어가는 자동화가 엄청 편한거였네요핵심은 'PDL에 원하는 패킷 이름과 자료형만 선언해주면 내가 원하는 패킷을 만들 수 있다' 인데 자동화의 저력 정말 감탄했습니다혹시 루키스님이 참고한 서적 같은 것도 알 수 있을까요? 아니면 회사를 다니시면서 분석하시면서 배운 코드신가요
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
ParseList에서 ParseMember를 호출하는데 왜 정상작동하는지 궁금합니다
훌륭한 강의 제공해주셔서 감사합니다 ParseList부분에서 ParseMember를 한번 더 호출하는데memberCode나 readCode와 같은 변수들은 스택에 있어서 초기화 되는거 아닌가요?? 정상값을 왜 뱉는거있지 모르겠습니다 재귀로 호출하더라도 먼저 호출한 ParseMember가 call stack에 쌓인 스택 프레임을 나중에 반환해서 인가요??
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
ReadLock 부분 질문입니다.
public void ReadLock() { while(true) { for(int i = 0 ; i < MAX_SPIN_COUNT; i++) { int expected = (_flag & READ_MASK); if(Interlocked.CompareExchange(ref _flag, expected + 1, expected) == expected)) return; } Thread.Yield(); } }해당 강의에서 작성해주신 ReadLock 부분 코드인데,ReaderWriterLock에 경우 특정 쓰레드가 WriteLock을 잡고 있을 경우 ReadLock을 사용하지 못한다고 알려주셨는데 int expected = (_flag & READ_MASK)위 코드는 Reader 부분만 긁어오니 Write를 잡고있는지 여부를 파악하지 않는데위에 if(_flag & WRITE_MASK >> 16 == 0)와 같은 Write를 잡고있는지 여부를 파악하는 조건문을 하나 추가해야하는 것 아닌가하는 의문이 남아 질문 남깁니다.항상 좋은 강의 감사합니다.
- 해결됨[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
cpu사용량 100%
해당 코드를 프로젝트에 맞게 변경 해서 쓰려고 하고 있는데 초보라서 버그를 못 잡고 있습니다. aws에 올려놓으면 특정 시점에 dev, live서버 모두 cpu 100%에 도달하고 cpu크레딧도 다 사용합니다.dev, live모두 같은 시점에 올라갈 때도 있고 아닐 때도 있습니다.인터넷에서 떠돌면서 공격 포인트를 찾는 해킹봇 때문인가 싶어서 클라에서 바로 연결을 끝내는 try catch쪽이랑 한동안 유의미한 패킷을 보내지 않으면 끊어버리는 timeout도 넣었는데도 발생합니다.wmi provider host문제는 아닌것 같아요 task manager기준으로는 서버 콘솔 프로그램이 많이 사용합니다.앗 그리고 이렇게 질문하는 이유는 초창기 코드가 많지 않을 때도 cpu 100%를 찍었기 때문입니다.코드는 강의 최종본을 변경해서 사용했습니다.Jetbrains dotTrace 를 사용해서 프로파일링을 한 경우 다음과 같이 뜹니다.
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
위치 동기화 검증 위치
전에 만드신 테라의 경우 맵 타일링 정보를 서버에 저장해두고 플레이어의 위치 동기화 로직을 서버에서 처리하셨나요?아니면 클라이언트가 먼저 이동(자기 기준)하고 그 결괏값을 서버로 보내는 방식으로 처리하셨나요?케이스 바이 케이스이겠지만 일반적으로 어떻게 하는 지 궁금해서 질문 드립니다.
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
17:13 부분 질문드립니다
i<5이기때문에 아무것도 출력되지 못하는게 맞는데 저는 헬로 스레드가 다섯번 나와버립니다..! 무슨 문제일까요?
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Clean함수의 else부분 질문 있습니다
_readPos = 0;_writePos = DataSize; 이렇게 하셨는데 순서가 반대 아닌가요??readPos가 2이고 writePos가 5였을 때 DataSize는 3그런데 readPos를 먼저 0으로 하면 DataSize는 5로 변해writePos는 3이 아닌 5로 초기화가 될것 같습니다else부분은 남은 데이터가 있는 경우 readPos만큼 Array의 처음 주소를 앞 당기는거니까 초기화된 writePos의 기대값으론 기존 writePos - readPos 아닌가요??readPos = 0;이 부분이 먼저 나온 이유가 궁급합니다!
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
Listener Init 함수를 여러번 호출 시킨 뒤 세션을 만들면 예기치 않은 동작을 할 수 있지 않나요??
Init 함수안에는 sessionFactory += sessionFactory;가 있는데Init을 여러번 호출 시키면 세션을 만드는 함수들이 _sessionFactory에 들어가서 Invoke 했을 때 여러개의 세션들이 만들어지는 예기치 않은 동작을 하지 않을까요??
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
RegisterRecv를 if 문에 넣는 이유
OnRecvCompleted함수에서 Listener와 다르게 Register 함수를 if문에 넣는 이유는 exception에 걸렸을 때를 생각해서 일부러 if문 안에 넣으신건가요??
- 해결됨[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
ReadLock 질문 드립니다!
ReadLock은 어떤 쓰레드도 write를 하고 있지 않을 때 ReadCount를 1 늘리는데 왜 while문 안에는 해당 조건문이 없는건가요??
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
콘솔프로그램으로 만든 서버 배포질문
보통 이렇게 콘솔프로그램으로 만든 서버는 어떻게 배포를 해야하나요? bin/Debug의 exe로 서버컴퓨터에서 실행시키지는 않을 것 같은데.... 따로 Setup파일을 만들어서 서버에 배포를 하는 건가요? 따로 Setup파일을 만든다면 어떤 방식으로 하는지도 궁굼합니다.!
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
receiveBuffer 부분 질문있습니다.
ReceiveBuffer 강의 듣는중 의문이 있어 문의드려요. TCP로 통신을할 경우 커널단에서 패킷 전체가 전송이 완료된후 OnReceive가 일어나는것으로 이해하고 있었는데해당강의 에서는 어플리케이션단에서 전체 패킷이 다 들어왔는지 확인 후 처리를 하는 로직이 들어가는거 같아 혼돈이 오는것 같습니다. 제가 이해한게 잘 못 되었을까요?TCP 단에서 전체 패킷이 다 도착한 후 OnReceive가 일어나는게 맞다면 어플리케이션단에서 다시 한번 확인 후 처리하는 이유가 있을까요? PacketSession에서 마지막에 client에서 데이터를 쓰기위해 sendBuffer에서 사용할 영역을 예약하는 open에 문의 사항이 있습니다.1) 강의대로 따라 하게되면 client에서 open(4096)을 하면 불필요하게 필요이상으로 버퍼를예약해서 실제 사용하지도 않지만 많이 예약하게되 _usedSize보다크게 되면서 계속해서 버퍼를 새로 만드는것으로 보이는데 실제 프로젝트에서는 필요한 사이즈만큼만 예약하게 되나요? 2) snedBuffer.Close() 에서 usedSize가 강의 에서는_usedSize = usedSize로 되어있는데 이러면 계속 같은 버퍼 공간에 덮어 씌어질거같은데_usedSize += usedSize로 되야 할거 같은데 이부분은 제가 잘못 이해 한건지 아니면 오타일까요?
- 해결됨[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
메모리가 줄줄 새고 있습니다
안녕하세요 메모리 릭과 관련해서 질문 드립니다비주얼 스튜디오에서 프로그램 실행시키면 진단 도구 창이 뜨고 그 안에 현재 프로세스가 얼마나 메모리 잡아 먹는지 보여주는데 콘솔에 로그 찍으면 메모리 계속 잡아먹으니까 서버와 클라 모두 콘솔에 아무것도 로그를 찍지 않게 빈 상태로 두었는데도 약 30초마다 3~5MB 씩 오릅니다 그래서 강의 자료에 있는 모든 코드를 복붙해서 어떤 로그도 안찍게 살짝 수정 후 실행했더니 결과는 동일하게 조금씩 오르더라구요 이 상태에서 서버는 계속 작동시키고 클라를 끄니까 메모리 릭 현상이 멈추긴 했습니다만이 현상이 선생님 환경에서도 일어나는지 궁금하네요..기본 서버 강의라서 아직 최적화가 덜 돼서 그런건지요..?
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
패킷 클래스 관련 질문입니다.
안녕하세요, Generator로 만든 클래스들(packetHander.cs나 GenPackets.cs, ~~Packetmanager.cs)을 보면 namespace가 없는데요, namespace가 없어도 다른 클래스에서 선언 및 사용이 가능한데요,혹시 namespace 없이 클래스를 선언해 사용하는 것과 namespace를 정의하고 클래스를 선언해 사용하는 것이 어떤 차이가 있는지 알 수 있을까요??그리고 namespace는 책꽂이 같은 개념이라 namespace를 using으로 선언하고 그 scope아래에 클래스를 만들면, 해당 클래스가 어디 위치에 있다는 것을 정의하는 개념으로 사용된다고 알고 있는데요, 이것도 맞을까요??
- 미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
안녕하세요 WriteLock의 Yield부분이 헷갈려 질문 드립니다
public void WriteLock() { int desired = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK; while (true) { for (int i = 0; i < MAX_SPIN_COUNT; i++) { if (Interlocked.CompareExchange(ref flag, desired, EMPTYFLAG) == EMPTY_FLAG) { return; } } Thread.Yield(); } }이코드에서 MAX_SPIN_COUNT까지 시도 해보고 Thread.Yield()로 다른 쓰레드에게 양보한다고 이해하고 있는데 여기서 다시 while(true)로 인해 또다시 반복해서 문을 두드리면 굳이 Yield()를 하는 이유가 무엇인지 모르겠습니다.