개인공부) 서버실습(35) - Packet Generator

Justin·2022년 7월 1일
1

서버공부

목록 보기
34/45

✅ 지난시간

숫자, 문자, 구조체와 같은 형태들을 어떻게 인코딩하여 패킷으로 넘겨주고, 디코딩하고 읽어올 수 있는지 Packet Session을 통해 제작해보았다.

🌐 Packet Generator

자동화는 하드코딩으로 진행하여 구성한 뒤 자동화할 수 있는 부분으로 변경하는 방식을 사용하면 좋다.

지금까지 하드코딩으로 하나하나 입력해주었지만, 이제 Packet Generator를 통해 자동화 처리를해주고자한다.

⚙ 환경설정

작업을 하기 위해 새로운 콘솔앱을 추가하고, Tools라는 폴더를 만들어 넣어둔다.

기존에 코드를 자동화 하기 위해서는 원본에 대한 정의가 필요하다. 여기서는 XML이 가독성이 더 좋기에, XML로 사용 패킷 정의 방식을 사용할 것이다.

  • JSON, XML, 자체 IDL 사용해도 무관

⛓ XML 작업

XML
XML이란 JSON과 비슷하게 데이터를 저장하고 전달하기 위한 수단이다. JSON에 비해 보안이 안정적이며 여러 인코딩들을 지원한다는 특징을 가지고 있다.

🔹 XML 추가

새항목 추가로 XML 파일을 추가한다.

추가 된 XML 파일 내에 넣고자 하는 데이터들을 담아주고, 이를 읽어오는 방식을 사용할 것인데 테스트를 위해 데이터를 먼저 담아준다.

<PDL>
	// packet이란 타입은 사용자가 직접 설정 할 수 있다.
	<packet name="Player InforReq">
    	<long name="playerId"/> // 한 줄이라면 
        // 바로 끝에 '/'를 넣어 닫아줄 수 도 있다.
		<string name="name"/>
		<list name ="skill">
			<int name="id"/>
			<short name="level"/>
			<float name="duration"/>
		</list>
    </packet> // <packet>을 통해 열었다면 </packet>으로 닫아주어야한다.
</PDL>

변환이 필요한 값들을 담아서 불러온다.

🔹 XML Read

XmlReaderSettings 선언하고 주석과 공백 무시 설정 한다.

 XmlReaderSettings settings = new XmlReaderSettings()
            {
                // 주석 무시
                IgnoreComments = true,
                // 스페이스바(공백) 무시
                IgnoreWhitespace = true,
            };

나중에는 경로설정을 해주겠지만 지금 테스트를 위해서는 PDL 파일이 실행파일 폴더에 있어야 한다.

XmlReader를 생성(Creat)해준다. 생성 해준 뒤에는 닫아(Dispose)주어야하는데 여기서 using문을 사용하여 알아서 닫아주게 만들 수 있다.

문장형태의 using
리소스에 액세스 하는 클래스들(File, Font)은 다 사용한 후에는 적절한 시기에 해제(Dispose)하여 해당 리소스(자원)을 다시 반납해야하는데, using을 사용하면 자동으로 Dispose를 한다

MoveToContent() 를 사용하면 앞의 헤더(Versiom, UTF-8) 부분을 넘기고 시작하게 한다.

 XmlReaderSettings settings = new XmlReaderSettings()
            {
                // 주석 무시
                IgnoreComments = true,
                // 스페이스바(공백) 무시
                IgnoreWhitespace = true,
            };

            // XML 을 생성하고 파싱 한 뒤에는 다시 닫아줘야 한다.
            // 이때 using을 사용한다면 해당 영역에서 벗어날 때 Dispose를 진행
            using (XmlReader x = XmlReader.Create("PDL.xml", settings)) 
            {
                // 버전 정보같은 헤더 영역은 건너 뛴다.
                x.MoveToContent();

                // 스트림 방식으로 읽어드림
                while(x.Read())
                {
                    if (x.Depth == 1 && x.NodeType == XmlNodeType.Element)
                        ParsePacket(x);
                    //Console.WriteLine(x.Name + " " + x["name"]);
                }
            }

depth에서 1과 같고, NoteType이 Elemnet(즉 시작 부분)인 경우에만 통과되도록 짠다.

depth
깊이 라는 뜻으로 한 단계 더 들어갈 경우 depth 값이 1 증가한다. <PDL> 부분의 depth가 0이라면 <packet name="PlayerInforReq"> 부분은 1이다. 그 안의 long, string 같은 경우는 2가된다.

  • 조건문 없이 주석 처리 된 부분으로 출력해보면 Xml에 저장된 내용을 불러오는걸 확인할 수 있다.

📒 Packet Format 작업

  • Packet Format Class 추가

기존에 하드코딩하며 인코딩, 디코딩했던 코드들을 복사하여 고정영역과 유동영역을 구분한다.

고정되는 부분은 그대로 두고, 사용하는 곳에 따라 달라지는 부분은 추후 대체될 수 있도록 {0}, {1} 이런 식으로 변경한다.

기존에 PlayerInfoReq로 Class에서 작성한 코드는 모두 가져와 작업하고 추가로 read, write의 인코딩, 디코딩 하는 부분도 가져와서 작업을 해준다.

string = @""
string을 선언하고 @"내용"해주면 내용을 예시들과 같이 길게 선언하고, 기호({}, () 등)를 모두 텍스트로 구분하게 할 수 있다.

  • 괄호{} 사용하고 싶으면 두 번 연속으로 사용해주면 된다.

실제 데이터가 들어가야할 부분이기에

read와 write 모두 유동적인 부분들은 대체 처리를 해준다.

🎭 PacketGenerator - ParsePacket()

위에서 선언만 해두고 아직 만들지 않은 함수를 추가로 제작한다.

이 함수는 패킷의 시작 지점인지 확인하고, 해당 패킷의 멤버들을 불러오는 역할을 한다.

확인 과정은 아래와 같다.

  • NodeType이 EndElemnt 인가
  • 이름이 내가 지정한 packet 인가
  • 값이 null이 아닌가

💻 PacketGenerator - ParseMmebrs()

이제 packet의 내부에있는 member들을 불러오기 위한 작업이다.

depth 값을 구해와 비교해주며 예외 사항들을 처리해준다.

 public static void ParseMembers(XmlReader x)
        {
            string packetName = x["name"];

            // 내부 멤버들은 뎁스가 하나 추가되니 +1
            int depth = x.Depth + 1;
            while(x.Read())
            {
                // 순서대로 불러오기에 /packet이 오면 depth가 1이라서 
                // 알아서 나가질 것
                if (x.Depth != depth)
                    break;

                string memberName = x["name"];  
                if(string.IsNullOrEmpty(memberName))
                {
                    Console.WriteLine("Member without name");
                    return;
                }
		}

예외사항들이 모두 걸리진 이후는 string, int 이런 타입 마다 인코딩, 디코딩 방식이 다르기 때문에 해당 타입별로 어떤 동작을 할 지를 정해주면 된다.

  • 타입은 정해져있는 것이 아닌 Xml에서 지정한 대로이다.
profile
인디 게임을 만들며 공부하고 있습니다.

0개의 댓글