select를 이용한 별움직이기 서버

2021. 11. 8. 20:20게임서버/namespace univ_dev

320x100

안녕하세요 대학생 개발자입니다.

 

음... 마지막으로 올렸던 글에서는 별 움직이기 클라이언트에 대한 정리를 했었는데요

오늘은 서버에 대해서 정리를 해보려고 합니다...(오늘 테스트 하는데 혹시 서버가 죽을까... 무서웠습니다)

 

프로토콜은 이전과 동일합니다. 대신에 다른점은 그냥 이번에는 메세지 안에 헤더가 포함되어있다는 점 뿐??... 뭐 코드에서 아주조금만 달라지면 똑같은 결과로 나오니까... 뭐 특이점은 없습니다.

 

먼저 얘도 TCP 소켓하나 만들어서 리슨소켓으로 쓸거구요.

+논블로킹 소켓으로 만들어줄겁니다.

+서버의 경우에는 linger를 이용해서 time_wait을 없엘겁니다.

그외에는 특이사항이 없기 때문에 따로 코드를 올리진 않을거구요.

 

먼저 서버의 로직의 경우에는 클라이언트와는 좀 다를겁니다.

먼저 플레이어들로부터 받는 패킷을 처리해야될겁니다. NetWorking이라고 부르겠습니다.

그리고 서버의 로직이 또 있을겁니다. 만약 충돌처리를 해야된다거나 혹은 길찾기 알고리즘 실행등 뭐 다양한것이 있겠지만... 일단은 통틀어서 Logic이라고 하겠습니다

그리고 클라이언트에는 Render의 기능이있었습니다. 하지만 서버는 필요가없죠..

하지만 저같은 경우에는... 서버에서도 클라이언트와 동일하게 작동되는지 확인을 위해서 Render를 넣었습니다.

그럼

NetWorking();

Logic();

Render(); 이런 순서로 가게될텐데...

이 서버에는 로직이 존재하지 않습니다. 그냥 패킷이 오면 그 패킷에 있는 id의 x와y값을 바로 바꿔버리면 그만이니까요.

그래서 NetWorking() , Render(); 두개만 놓고 보겠습니다.

 

정말 안타깝게 주석이 모든걸 설명해버리고 있습니다...

얘도 따로 writeSet은 존재하지 않습니다. 클라이언트와 마찬가지로 이 테스트 환경에서는 절대 송신 버퍼가 꽉찰 확률이 전혀 없다고 판단했기 때문입니다.

셋을 초기화하고 리슨소켓 넣고 플레이어리스트를 돌면서 하나씩 셋에 넣어주는 모습입니다.

 

다음은 나머지 로직들입니다.

select를 해서 set이 된 소켓들만 작업을 하는데요 listen socket의 경우에는 set이 되어있다면

backlog에 누군가가 들어왔고 그걸 accept queue에 넣어뒀다는 얘기가 될텐데요.

그경우에는 accept를 호출해야되겠죠...

AcceptProc의 경우에는 accept부터 플레이어 생성, 아이디 패킷 전송, 플레이어 별생성 패킷전송, 타유저들 별생성 패킷전송 등의 작업을 하고있습니다. 뭐... 이건 굳이 제가 코드를 안까도 충분히 구현이 가능한 부분이기때문에 넘어가겠습니다.

플레이어가 들어왔을때

1. 아이디를 보낸다.

2. 플레이어에게 플레이어 별생성을보낸다.

3. 타플레이어에게 플레이어 별생성을보낸다.

4. 플레이어에게 타플레이어 별생성을보낸다.

 

2,4를 묶으셔도 좋고 2,3을 묶으셔도 좋습니다. 대신에 2,3을묶고 또 2,4를 묶게되면... 문제가되겠죠 그부분만 조심하세요.

저같은 경우에는 4번을 먼저하고 그다음에 g_PlayerList에 push_back한뒤 모두에게 플레이어 별생성이라는 메세지가 가도록 설계했습니다.

 

그리고 리슨소켓을 제외한 다른 소켓들 즉 이제 클라이언트들이죠 클라이언트들이 Set되어있다면... 그 클라이언트들로부터 recv할게 있다는 의미일겁니다.

그때는 recv하고 그 recv한 메세지에 따라서 행동을 해주면 되는데 안타깝게 클라이언트에서 보낼 수 있는 메세지는 MESSAGE_MOVE 밖에 없습니다. 그래서 그냥 recv호출한뒤 바로 그거가지고

Player->x = packet->x;

Player->y = packet->y;

정말 이작업밖에 없습니다.

물론 온 데이터를 이 사람 외에 다른 사람에게 모두 보내는 처리 또 예외처리나 에러처리는 하겠지만요...

ㅋㅋㅋㅋ 그래도 양심은 있습니다. 헤더에 타입에 따라서 스위치 분기타게는 해놨습니다...

SendBroadcast함수 같은 경우에는 Player*하나와 보낼 버퍼 그리고 버퍼의 사이즈를 받는데요

저렇게 받는 이유는 어차피 send의 인자로 char*가 들어가야되기때문에... 도있고 그리고 받았던 데이터가 char*이기 때문에... 바로 넣을수 있어서 좋기때문이기도 합니다.

아 Broadcast의 Player*는 그 플레이어를 제외한 모두에게 보내라가 되고 뭐... 없는 포인터거나 혹은 널포인터면 그냥 모두에게 보내는겁니다.

 

SendBroadcast함수 내부인데요... 뭐... 별거없습니다 하나씩 돌면서 그게 안보낼 애면 컨티뉴고 나머지는 다 SendUnicast합니다. SendUnicast의 경우에는 그냥 send만 하는함수입니다.

 

네... 이렇습니다 저기 g_RemovePlayerList는 list같지만 사실 set입니다... list는 중복으로 들어가도 되지만 set의 경우에는 똑같은 값은 하나만 있다는게 보장되기 때문이죠... 만약 send에서 에러나서 저기 넣었는데 recv에서 또 에러가 나서 저기 넣는다면..? 흠... 어쩔수없죠 두번 지우게 되는거니까 아마 delete 에서 펑 터질겁니다. 그걸 방지하려고 set으로 간거에요...

 

 

사실 아까 로직에 NetWorking, Render만 있다고 말씀드렸는데 얘도있습니다.

아까 set에 담아뒀던 녀석들과 연결을 끊어줄 함수입니다. 저같은 경우에는 문제가 생긴 그자리에서 바로 disconnect를 하기보다는 그 녀석들을 한꺼번에 모아서 프레임의 마지막에 싹 정리해주는 방식을 채택해서 만들었습니다.

뭐... 돌면서 하나씩 클로즈소켓하고 플레이어 리스트에서 제거한 다음 MESSAGE_DELETE_STAR 메세지 만든다음에 브로드캐스팅을 돌립니다... 뭐 사실 리스트에서 빠졌으니 temp를 안넣고 nullptr을 넣어도 마찬가지겠지만 뭐 똑같으니 그냥 뭘 넣든간에 라고 생각하시면 될것같아요. 그리고 마지막으로 set을 초기화 시켜버리고 나가버립니다. ㅋㅋㅋ 저렇게 만들꺼면 bool을 왜 반환형으로 했는지 모르겠는데... 그냥 winsocket api들을 보니까 대부분 BOOL이나 int 타입의 반환형이 존재하더라구요... 저경우에는 딱히 실패의 케이스는 없긴한데... 그래도 일단은 비슷하게 만들려다 보니까 저렇게 했는데 별 의미없는 반환이 되어버렸습니다.

 

네... 이번에는 select로 별움직이기 서버를 만들어봤는데요 이경우에는 따로 서버의 로직이 없다보니 서버가 오히려 클라이언트보다 쉬웠던것 같습니다...

흠... 째뜬.. 오늘도 긴글 읽어주셔서 정말 감사드립니다. 그럼 다음엔 더 유용한 정보로 찾아뵙겠습니다

그럼 안녕히계세요

320x100