서버 공부

[서버 공부]17.Packet Generator 1,2

myjeongjun 2025. 1. 24. 01:31

이번에는 패킷을 클래스로 정의해두고 보내는 것이 아닌 XML로 태그와 속성들을 정해두고

따로 Format들도 만들어서 패킷을 자동으로 만들어주는 코드를 짜려고한다. 

 

솔직히 이 부분은 처음 보는것들이 꽤나있어서 제대로 서술할 수있을지 모르겠다.

우선 XML을 이용해서 기존의 PlayerInfoReq패킷에 들어있어야할 속성들을 전부 정의해준다.

 

<?xml version="1.0" encoding="utf-8" ?>
<PDL>
	<packet name  ="PlayerInfoReq">
		<long name ="playerID"/>
		<string name ="name"/>
		<list name ="skill">
			<int name ="id"/>
			<short name ="level"/>
			<float name ="duration"/>
		</list>
	</packet>
</PDL>

 

 

이제 아래의 코드로 한줄식 읽어올텐데,

1.Create라는 함수를 사용해서 xml파일을 불러와주고, XmlReader에 저장해준다.

 

2.MoveToContent는 유효한 데이터가 있는 지점으로 이동시키는 함수이다.

 

3.stream형식으로 데이터를 읽어오므로 한줄씩 내려가는데, r.Name은 정의해놓은 tag(int, long ,list같은)이고

r.["name"]은 name이라는 속성의 속성값을 읽어온다.

 

4.depth는 노드의 깊이를 나타내는데, 데이터가 계층적 구조로 이루어져있기때문에 자식 노드로 내려갈수록 depth값도 올라간다.

 

 

5.XML의 태그의 시작과 종료를 나타내는 법은 크게 2가지로

			<float name ="duration"/>

 

			<float name ="duration">
				
			</float>

 

여기서 </태그이름>을 해주게되면 EndElement로 인식한다.

 

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

            using (XmlReader r = XmlReader.Create("PDL.xml", settings)) 
            {
                //유효한 데이터가 있는 지점으로 이동
                r.MoveToContent();

                //stream형식으로 한줄씩 읽어옴
                while (r.Read())
                {
                    //PDL은 depth가 0임
                    if (r.Depth == 1 && r.NodeType == XmlNodeType.Element)
                    {
                        ParsePacket(r);
                    }

                    //Console.WriteLine(r.Name + " " + r["Name"]);
                }

                File.WriteAllText("GeneratePacket.cs", genPackets);
            }
        }

 

위의 코드를 정리하자면 "PDL.xml파일에서 한 줄씩 읽으면서 깊이가 1이고 Element 타입이면 ParsePacket함수로 Parsing 해준다." 정도겠다. 

 

다음으로 string.Format함수를 이용해서 사전에 정의해놓은 형식에 유연하게 값을 넣어 코드를 짠다. 아래는 이전 글에서 보인 Raed나 Write함수인데,우리가 생성할 패킷들은 이러한 함수들이 내부에 들어있어야한다.

(일종에 C#으로치면 Generic인데 이건 Class에 한정되지않고 코드 전체의 유연성을 높여버렸다.)

 

@""을 사용하면 줄바꿈이나 공백도 전부 포함할수 있다. 

 

아래는 어떤 패킷인지에따라 패킷 id나 이름, 함수등을 정의해놓은 문자열로

{i}에 값을 대입해서 코드를 만들어준다.

 

{0} 어떤 패킷인가?

{1} 이 패킷에는 어떤 변수들이 들어있는가?

{2} 이 변수를 Read시키기 위한 함수

-> 이걸 따로 정의해주는 이유는 만약 변수가 long이면 8바이트 만큼을 버퍼 배열에 넣어줘야할것이고, int는 4바이트를 넣어주듯이 각각 크기가 다르다. 또한 string같은 경우는 정해져있는 크기가 없기때문에 패킷에 들어갈 정보에 유연하게 대처하기 위해 더 세부적으로 조작하므로 따로 정의한다.

{3} Write또한 따로 정의 (Read와 동일한 이유)

        //{0} 패킷 이름
        //{1} 멤버 변수들
        //{2} 멤버 변수 Raed
        //{3} 멤버 변수 Write
        public static string packetFormat =
@"
  class {0} 
{{
    {1}



    public  void Read(ArraySegment<byte> segment)
    {{
        ushort count = 0;

        ReadOnlySpan<byte> s = new ReadOnlySpan<byte>(segment.Array, segment.Offset, segment.Count);

        //ushort size = BitConverter.ToUInt16(s.Array, s.Offset);
        count += sizeof(ushort); ;
        //ushort ID = BitConverter.ToUInt16(s.Array, s.Offset + count);
        count += sizeof(ushort); ;
        
        {2}


    }}

    public  ArraySegment<byte> Write()
    {{
        ArraySegment<byte> segment = SendBufferHelper.Open(4096);
        ushort count = 0;
        bool success = true;

        Span<byte> s = new Span<byte>(segment.Array, segment.Offset, segment.Count);

        //success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset, s.Count), packet.size);
        count += sizeof(ushort);
        success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), (ushort)PacketID.{0});
        count += sizeof(ushort);

        {3}
        success &= BitConverter.TryWriteBytes(s, count);
        if (success = false)
            return null;

        return SendBufferHelper.Close(count);
    }}
}}

";

 

 

따로 정의 해준 Raed함수 (long,int,float등에 사용되는 readFormat과 string에게 사용하는 readStringFormat함수)

        //{0} 변수 이름
        //{1} To- 변수 형식
        //{2} 변수 형식
        public static string readFormat =
@"this.{0} = BitConverter.{1}(s.Slice(count, s.Length - count));
count += sizeof({2});";

        //{0} 변수 이름
        public static string readsStringFormat =
@"ushort {0}Len = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
count += sizeof(ushort);
this.{0} = Encoding.Unicode.GetString(s.Slice(count, nameLen));
count += {0}Len;";

 

이후에는 단순 반복 잡업을 통해서 패킷 형식들을 정의해야하므로 더 길게 쓰진않겠다.

 

결과

'서버 공부' 카테고리의 다른 글

[서버 공부]19. Job Queue1,2  (0) 2025.01.28
[서버 공부]18. Packet Generator 3,4,5,6  (0) 2025.01.27
[서버 공부]16.PacketSession  (0) 2025.01.16
[서버 공부]15.SendBuffer  (0) 2025.01.15
[서버 공부]14. RecvBuffer  (0) 2025.01.15