๐Ÿ™‡โ€โ™€๏ธSerialization

์ง๋ ฌํ™”

  • ๋ฉ”๋ชจ๋ฆฌ ์ƒ์˜ ์–ด๋–ค ์ธ์Šคํ„ด์Šค๋กœ ์กด์žฌํ•˜๋Š” ์• ๋ฅผ ๋‚ฉ์ž‘ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์„œ ๋ฒ„ํผ์•ˆ์— ๋ฐ€์–ด๋„ฃ๋Š” ์ž‘์—… : Serialization
    • ์„ธ์ด๋ธŒ ํŒŒ์ผ์„ ๋งŒ๋“ค ๋•Œ๋„ serialization ์‚ฌ์šฉ
  • ๋ฐ”์ดํŠธ ๊ณ„์—ด์— ์žˆ๋Š” ์• ๋ฅผ ๊บผ๋‚ด ์“ฐ๋Š” ๊ฒƒ : Deserialization

๐Ÿช์ฐจ๊ทผ์ฐจ๊ทผ

  • Serialization1 - TryWriteBite(Span)

    • ๋ฉ”๋ชจ๋ฆฌ ์ƒ์˜ ์–ด๋–ค ์ธ์Šคํ„ด์Šค๋กœ ์กด์žฌํ•˜๋Š” ์• ๋ฅผ ๋‚ฉ์ž‘ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์„œ ๋ฒ„ํผ์•ˆ์— ๋ฐ€์–ด๋„ฃ๋Š” ์ž‘์—… : Serialization
      • ์„ธ์ด๋ธŒ ํŒŒ์ผ์„ ๋งŒ๋“ค ๋•Œ๋„ serialization ์‚ฌ์šฉ
    • ๋ฐ”์ดํŠธ ๊ณ„์—ด์— ์žˆ๋Š” ์• ๋ฅผ ๊บผ๋‚ด ์“ฐ๋Š” ๊ฒƒ : Deserialization

    • ํด๋ผ์—์„œ ์„œ๋ฒ„๋กœ ์ •๋ณด๋ฅผ ์ค„ ๋•Œ : playerId ์ฃผ๊ธฐ
    • ์„œ๋ฒ„์—์„œ ํด๋ผ๋กœ ์ •๋ณด๋ฅผ ์ค„ ๋•Œ : hp, attack ์ฃผ๊ธฐ

    • Recvํ•  ๋• ๋ฐ›์€ ๋ฐฐ์—ด์—์„œ ๋ฝ‘์•„์˜ค๊ธฐ

    • PlayerInfoReq๋ฅผ ๋งŒ๋“ค์–ด์„œ ํด๋ผ์—์„œ ์„œ๋ฒ„๋กœ ์ •๋ณด ( playerId)๋ฅผ ์ฃผ๋Š” ์ƒํ™ฉ์ผ ๋•Œ
      • SendBufferHelper.Open์œผ๋กœ ๊ณต๊ฐ„์„ ์—ด๊ณ  BitConverter๋กœ size packetId playerId๋ฅผ ๋ฐ”์ดํŠธ ๋ฐฐ์—ด๋กœ ์ €์žฅ Open์œผ๋กœ ์—ด์–ด์ค€ ArraySegment์— ๋ณต์‚ฌ๋ฅผ ํ•˜๊ณ  Close ํ›„ Send ์ž‘์—…
    • ์œ„์˜ Send์˜ ๋‹จ์ 
      • size packetId playerId๋ฅผ ๋ฐ”์ดํŠธ ๋ฐฐ์—ด๋กœ ์ €์žฅํ•˜๋Š” ๋ถ€๋ถ„์ด ๊ฐ๊ฐ ๋™์ ์œผ๋กœ ๋ฐฐ์—ด์„ ์ƒˆ๋กœ ์ƒ์„ฑํ•œ ํ›„ Copy๋ฅผ ํ†ตํ•ด OpenSegment์— ๋„ฃ๋Š”๊ฒŒ ์•„์‰ฌ์›€
      • ๋ง‰๋ฐ”๋กœ OpenSegment์— ๋„ฃ์œผ๋ฉด ์ข‹์„ ๋“ฏ => BitConverter.TryWriteBites( new Span(s.Array, s.Offset, s.Count ), packet.size); ๋ฅผ ์‚ฌ์šฉ
      • TryWriteBites๋Š” ๊ฐ’์„ ๋„ฃ์„ ๋„์ฐฉ์ง€์™€ ๋„ฃ์„ ๊ฐ’์„ ์ธ์ž๋กœ ๋ฐ›๋Š”๋ฐ ๋„์ฐฉ์ง€๋ฅผ Span์ด๋ผ๋Š” ๊ฒƒ์œผ๋กœ ๋ฐ›๋Š”๋‹ค, bool๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ success์—ฌ๋ถ€๋ฅผ ํŒ๋‹จ ํ•  ์ˆ˜ ์žˆ๋‹ค, Copy๋ถ€๋ถ„๊ณผ ๋ฐฐ์—ด์„ ๋งŒ๋“œ๋Š” ๋ถ€๋ถ„์€ ์ œ๊ฑฐ
    • size๋Š” ๊ฒฐ๊ตญ ๋‹ค ๊ณ„์‚ฐํ•œ ๋’ค์— ์•Œ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ success &= BitConverter.TryWriteBites( new Span(s.Array, s.Offset, s.Count ), packet.size);๋Š” ๋งˆ์ง€๋ง‰์— ๋„ฃ์–ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค
  • Serialization2 - Serialize, Deserialize ๊ฐœ์„ 

    • Openํ›„ TryWriteBytes๋กœ ๋ฐ€์–ด๋„ฃ์–ด์ฃผ๋Š” ๋ถ€๋ถ„์„ ํ•จ์ˆ˜ํ˜•ํƒœ๋กœ ๋งŒ๋“ค๊ธฐ -> public abstract ArraySegment Write(), public abstract void Read(ArraySegment s)

    • Write์— TryWriteBytes๋ถ€๋ถ„์„ ๋„ฃ์–ด์ฃผ๊ธฐ

    • ArraySegment๋Š” operate == ๋กœ ์˜ค๋ฒ„๋กœ๋”ฉ์„ ํ•ด์„œ null๋กœ ๊ตฌ๋ณ„๊ฐ€๋Šฅ

    • Read๋ถ€๋ถ„์€ size id๋ฅผ ์ฝ์€ ๋’ค์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ์ด๋ฏ€๋กœ size id๋Š” ์ฃผ์„์ฒ˜๋ฆฌ, playerId๋งŒ ์‚ฌ์šฉ

    • ํŒจํ‚ท ํ—ค๋”์— ์žˆ๋Š” ๊ฐ’์€ ๋ฏฟ์„ ์ˆ˜ ์—†๊ณ  ์ฐธ๊ณ ๋งŒ ํ•ด์•ผ ๋จ

    • 12๋ฐ”์ดํŠธ๊ฐ€ ์™€์•ผํ•˜๋Š”๋ฐ 4๋ฐ”์ดํŠธ๋งŒ ์œ ํšจ๋ฒ”์œ„๋ผ๊ณ  ์ง‘์–ด์ฃผ์–ด๋„ ๋’ค์— ๋ถ€๋ถ„์ด ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ์œผ๋ฏ€๋กœ 12๋ฐ”์ดํŠธ๊นŒ์ง€์˜ ๋‚ด์šฉ์ด ์ถœ๋ ฅ๋˜๊ณ  size๋Š” 4๋ผ๊ณ  ๋œฌ๋‹ค

      • => Read๋ถ€๋ถ„ ๋งˆ์ง€๋ง‰์— BitConverter.ToInt64(new ReadOnlySpan(s.Array, s.Offset + count, s.Count - count));๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฒ”์œ„๋ฅผ ์ดˆ๊ณผํ•œ ๋…€์„๋“ค์€ Exeption์ด ๋œจ๊ฒŒ ๋งŒ๋“ ๋‹ค

      • => ์ด๋ ‡๊ฒŒ ํ•œ๋‹ค๋ฉด ๋งˆ์ง€๋ง‰์— size๊ฐ’์„ ์ž‘๊ฒŒ ์ค€๋‹คํ•˜๋”๋ผ๋„ ๋‚จ์€ ๊ณต๊ฐ„์ด ๋ถ€์กฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— e๋ฐœ์ƒ

  • Serialization3 - ๊ฐ€๋ณ€์ ์ธ ๋ฐ์ดํ„ฐ

    • Span์˜ Sliceํ™œ์šฉ : ์ฒซ SendBufferHelper.Open์˜ ArraySegment๋ฅผ Span์œผ๋กœ ๋‹ค์‹œ ๋งŒ๋“คํ›„ TryWriteByte๋ถ€๋ถ„์„ Slice๋กœ ๋„ฃ์–ด ๊ฐ€๋…์„ฑ ๋†’์ด๊ธฐ, Slice๋Š” ๊ทธ ์ž์ฒด๊ฐ€ ๋ณ€ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ Span์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค -> ์‹ค์งˆ์ ์œผ๋กœ s์˜ ๋ณ€ํ™”๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค

    • Read๋ถ€๋ถ„์€ ReadOnlySpan์œผ๋กœ ๋งŒ๋“ค๊ธฐ

    • ๊ฐ€๋ณ€์ ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ๋„˜๊ธฐ๊ณ  ์ถ”์ถœํ• ๊นŒ?

      • 2๋ฐ”์ดํŠธ์งœ๋ฆฌ๋กœ string์˜ ํฌ๊ธฐ(string len [2])๊ฐ€ ์–ผ๋งˆ์ธ์ง€ ๋ณด๋‚ด๊ณ  ํ•ด๋‹นํ•˜๋Š” ํฌ๊ธฐ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์–ด์„œ ๋ณด๋‚ด๊ธฐ

      • name = โ€˜ABCDโ€™ ์ผ ๋•Œ this.name.Length๋ฅผ ํ•˜๋ฉด ํฌ๊ธฐ๋Š” 4์ด์ง€๋งŒ ๋ฐ”์ดํŠธ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด 8๋ฐ”์ดํŠธ๋กœ ๋œ๋‹ค : UTF-16์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค => ushort nameLen = (ushort)Encoding.Unicode.GetByteCount(this.name);์œผ๋กœ ํ•ด์•ผ๋œ๋‹ค

      • Read๋ถ€๋ถ„์€ nameLen์„ ToUInt16์œผ๋กœ ๋ฝ‘์•„์˜ค๊ณ  string๋ฐ์ดํ„ฐ๋Š” Encoding.Unicode.GetString()์„ ์‚ฌ์šฉํ•จ : ๋ฐ”์ดํŠธ๋ฅผ string์œผ๋กœ ๋ฐ”๊ฟ”์คŒ

Encoding.Unicode.GetString() => byte๋ฅผ string์œผ๋กœ ๋ณ€ํ™˜. Encoding.Unicode.GetBytes() => string๋ฅผ byte์œผ๋กœ ๋ณ€ํ™˜.

//string
//ushort nameLen = (ushort)Encoding.Unicode.GetByteCount(this.name);
//success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
//count += sizeof(ushort);
//Array.Copy(Encoding.Unicode.GetBytes(this.name), 0, segment.Array, count, nameLen);
//count += nameLen;

// ์œ„์— ์ฝ”๋“œ๋ฅผ ๊ฐœ์„ ํ•œ ๋ถ€๋ถ„
ushort nameLen = (ushort)Encoding.Unicode.GetBytes(this.name, 0, this.name.Length, segment.Array, segment.Offset + count + sizeof(ushort));
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
count += sizeof(ushort);
count += nameLen;
  • Serialization4 - List Serialization

    • ๋ฆฌ์ŠคํŠธ๋Š” ์–ด๋–ป๊ฒŒ ๋ฐ€์–ด์ฃผ๊ณ  ์ถ”์ถœํ• ๊นŒ?

      • ๋ฆฌ์ŠคํŠธ๊ฐ€ int๊ฐ™์€๊ฒƒ์„ ๋ฐ›์„ ๋• string์„ ๋ฐ›์„ ๋•Œ ์ฒ˜๋Ÿผ ํฌ๊ธฐ๋ฅผ ๋ฐ›๊ณ  ๋ฐ€์–ด์ฃผ๊ณ  ์ถ”์ถœ๊ฐ€๋Šฅ

      • ํ•˜์ง€๋งŒ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฐ›์„๊ฒฝ์šฐ ๋‹ฌ๋ผ์ง

        • ๊ตฌ์ดˆ์ œ์˜ ํฌ๊ธฐ๋งŒํผ ๋ฐ€์–ด๋„ฃ์–ด์ฃผ๊ณ  ๋˜ ๊ตฌ์กฐ์ฒด๋‚ด์˜ ์ธ์ž๋“ค์„ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ๊ฐ ๋ฐ€์–ด๋„ฃ์–ด์คŒ

        • Read๋ถ€๋ถ„๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ธ์ž๊ฐ€ ๋ช‡๊ฐœ๋“ค์–ด๊ฐ€ ์žˆ๋Š”์ง€ ์ถ”์ถœํ•˜๊ณ  ์ˆœํšŒํ•˜๋ฉด์„œ ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์ถ”์ถœํ›„ ์ถ”๊ฐ€ ํ•ด์คŒ

public struct SkillInfo
{
    public int id;
    public short level;
    public float duration;

    public bool Write(Span<byte> s, ref ushort count)
    {
        bool success = true;
        success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), id);
        count += sizeof(int);
        success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), level);
        count += sizeof(short);
        success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), duration);
        count += sizeof(float);
        return success;
    }

    public void Read(ReadOnlySpan<byte> s, ref ushort count)
    {
        id = BitConverter.ToInt32(s.Slice(count, s.Length - count));
        count += sizeof(int);
        level = BitConverter.ToInt16(s.Slice(count, s.Length - count));
        count += sizeof(short);
        duration = BitConverter.ToSingle(s.Slice(count, s.Length - count));
        count += sizeof(float);
    }
}

// skill list
skills.Clear();
ushort skillLen = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
count += sizeof(ushort);
for (int i = 0; i < skillLen; i++)
{
    SkillInfo skill = new SkillInfo();
    skill.Read(s, ref count);
    skills.Add(skill);
}

// skill list
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), (ushort)skills.Count);
count += sizeof(ushort);
foreach (SkillInfo skill in skills)
    success &= skill.Write(s, ref count);
  
  • Serialize๋ฅผ ํ•  ๋•Œ ๋ณดํ†ต ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์€ ๊ตฌ๊ธ€์—์„œ ์ง€์›ํ•˜๋Š” ํ”„๋กœํ† ๋ฒ„ํ”„ ํ”Œ๋žซ๋ฒ„ํ”„ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•จ

    • ํ”„๋กœํ† ๋ฒ„ํ”„๋Š” ์ค‘๊ฐ„์— ์–ด๋–ค ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฑ„์›Œ์„œ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…์„ ํ•จ : ์„ฑ๋Šฅ์€ ์†ํ•ด๊ฐ€ ์žˆ์–ด๋„ ์ฝ”๋”ฉ์ด ํŽธํ•จ, ์ง๊ด€์ ์ž„

    • ํ”Œ๋žซ๋ฒ„ํ”„๋Š” ๋ง‰๋ฐ”๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ดํŠธ ๋ฐฐ์—ด์— ์ง‘์–ด ๋„ฃ์Œ

๐Ÿช์ฝ”๋“œ

ServerSesion

namespace DummyClient
{
    public abstract class Packet
    {
        public ushort size;
        public ushort packetId;

        public abstract ArraySegment<byte> Write();
        public abstract void Read(ArraySegment<byte> s);
    }

    class PlayerInfoReq : Packet
    {
        public long playerId;
        public string name;

        public struct SkillInfo
        {
            public int id;
            public short level;
            public float duration;

            public bool Write(Span<byte> s, ref ushort count)
            {
                bool success = true;
                success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), id);
                count += sizeof(int);
                success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), level);
                count += sizeof(short);
                success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), duration);
                count += sizeof(float);
                return success;
            }

            public void Read(ReadOnlySpan<byte> s, ref ushort count)
            {
                id = BitConverter.ToInt32(s.Slice(count, s.Length - count));
                count += sizeof(int);
                level = BitConverter.ToInt16(s.Slice(count, s.Length - count));
                count += sizeof(short);
                duration = BitConverter.ToSingle(s.Slice(count, s.Length - count));
                count += sizeof(float);
            }
        }

        public List<SkillInfo> skills = new List<SkillInfo>();

        public PlayerInfoReq()
        {
            this.packetId = (ushort)PacketID.PlayerInfoReq;
        }

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

            ReadOnlySpan<byte> s = new ReadOnlySpan<byte>(segment.Array, segment.Offset + count, 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);
            this.playerId = BitConverter.ToInt64(s.Slice(count, s.Length - count));
            count += sizeof(long);

            // string
            ushort nameLen = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
            count += sizeof(ushort);
            this.name = Encoding.Unicode.GetString(s.Slice(count, nameLen));
            count += nameLen;

            // skill list
            skills.Clear();
            ushort skillLen = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
            count += sizeof(ushort);
            for (int i = 0; i < skillLen; i++)
            {
                SkillInfo skill = new SkillInfo();
                skill.Read(s, ref count);
                skills.Add(skill);
            }
        }

        public override 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 + count, segment.Count - 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), this.packetId); // s๊ฐ€ ๋ณ€ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ Sliceํ•˜์—ฌ Span์„ ๋งŒ๋“ค์–ด์„œ ๋„ฃ์–ด์คŒ
            count += sizeof(ushort);
            success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.playerId);
            count += sizeof(long);

            //string
            //ushort nameLen = (ushort)Encoding.Unicode.GetByteCount(this.name);
            //success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
            //count += sizeof(ushort);
            //Array.Copy(Encoding.Unicode.GetBytes(this.name), 0, segment.Array, count, nameLen);
            //count += nameLen;

            ushort nameLen = (ushort)Encoding.Unicode.GetBytes(this.name, 0, this.name.Length, segment.Array, segment.Offset + count + sizeof(ushort));
            success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
            count += sizeof(ushort);
            count += nameLen;

            // skill list
            success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), (ushort)skills.Count);
            count += sizeof(ushort);
            foreach (SkillInfo skill in skills)
                success &= skill.Write(s, ref count);

            success &= BitConverter.TryWriteBytes(s, count);

            if (success == false)
                return null;

            return SendBufferHelper.Close(count);
        }
    }

    public enum PacketID
    {
        PlayerInfoReq = 1,
        PlayerInfoOk = 2,
    }

    class ServerSession : Session
    {
        public override void OnConnected(EndPoint endPoint)
        {
            Console.WriteLine($"OnConnected : {endPoint}");

            PlayerInfoReq packet = new PlayerInfoReq() { playerId = 1001 , name = "ABCD"};
            packet.skills.Add(new PlayerInfoReq.SkillInfo() { id = 101, level = 1, duration = 3.0f });
            packet.skills.Add(new PlayerInfoReq.SkillInfo() { id = 201, level = 2, duration = 4.0f });
            packet.skills.Add(new PlayerInfoReq.SkillInfo() { id = 301, level = 3, duration = 5.0f });
            packet.skills.Add(new PlayerInfoReq.SkillInfo() { id = 401, level = 4, duration = 6.0f });

            // ๋ณด๋‚ธ๋‹ค
            //for (int i = 0; i < 5; i++)
            {
                ArraySegment<byte> s = packet.Write();
                if (s != null)
                    Send(s);
            }
        }

        public override void OnDisconnected(EndPoint endPoint)
        {
            Console.WriteLine($"OnDisconnected : {endPoint}");
        }

        public override int OnRecv(ArraySegment<byte> buffer)
        {
            string recvData = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
            Console.WriteLine($"[From Server] {recvData}");

            return buffer.Count;
        }

        public override void OnSend(int numOfBytes)
        {
            Console.WriteLine($"Transferred bytes : {numOfBytes}");
        }
    }
}

ClientSession

namespace Server
{
    public abstract class Packet
    {
        public ushort size;
        public ushort packetId;

        public abstract ArraySegment<byte> Write();
        public abstract void Read(ArraySegment<byte> s);
    }

    class PlayerInfoReq : Packet
    {
        public long playerId;
        public string name;

        public struct SkillInfo
        {
            public int id;
            public short level;
            public float duration;

            public bool Write(Span<byte> s, ref ushort count)
            {
                bool success = true;
                success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), id);
                count += sizeof(int);
                success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), level);
                count += sizeof(short);
                success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), duration);
                count += sizeof(float);
                return success;
            }

            public void Read(ReadOnlySpan<byte> s, ref ushort count)
            {
                id = BitConverter.ToInt32(s.Slice(count, s.Length - count));
                count += sizeof(int);
                level = BitConverter.ToInt16(s.Slice(count, s.Length - count));
                count += sizeof(short);
                duration = BitConverter.ToSingle(s.Slice(count, s.Length - count));
                count += sizeof(float);
            }
        }

        public List<SkillInfo> skills = new List<SkillInfo>();

        public PlayerInfoReq()
        {
            this.packetId = (ushort)PacketID.PlayerInfoReq;
        }

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

            ReadOnlySpan<byte> s = new ReadOnlySpan<byte>(segment.Array, segment.Offset + count, 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);
            this.playerId = BitConverter.ToInt64(s.Slice(count, s.Length - count));
            count += sizeof(long);

            // string
            ushort nameLen = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
            count += sizeof(ushort);
            this.name = Encoding.Unicode.GetString(s.Slice(count, nameLen));
            count += nameLen;

            // skill list
            skills.Clear();
            ushort skillLen = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
            count += sizeof(ushort);
            for (int i = 0; i < skillLen; i++)
            {
                SkillInfo skill = new SkillInfo();
                skill.Read(s, ref count);
                skills.Add(skill);
            }
        }

        public override 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 + count, segment.Count - 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), this.packetId); // s๊ฐ€ ๋ณ€ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ Sliceํ•˜์—ฌ Span์„ ๋งŒ๋“ค์–ด์„œ ๋„ฃ์–ด์คŒ
            count += sizeof(ushort);
            success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.playerId);
            count += sizeof(long);

            //string
            //ushort nameLen = (ushort)Encoding.Unicode.GetByteCount(this.name);
            //success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
            //count += sizeof(ushort);
            //Array.Copy(Encoding.Unicode.GetBytes(this.name), 0, segment.Array, count, nameLen);
            //count += nameLen;

            ushort nameLen = (ushort)Encoding.Unicode.GetBytes(this.name, 0, this.name.Length, segment.Array, segment.Offset + count + sizeof(ushort));
            success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), nameLen);
            count += sizeof(ushort);
            count += nameLen;

            // skill list
            success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), (ushort)skills.Count);
            count += sizeof(ushort);
            foreach (SkillInfo skill in skills)
                success &= skill.Write(s, ref count);

            success &= BitConverter.TryWriteBytes(s, count);

            if (success == false)
                return null;

            return SendBufferHelper.Close(count);
        }
    }

    public enum PacketID
    {
        PlayerInfoReq = 1,
        PlayerInfoOk = 2,
    }

    class ClientSession : PacketSession
    {
        public override void OnConnected(EndPoint endPoint)
        {
            Console.WriteLine($"OnConnected : {endPoint}");

            //Packet packet = new Packet() { size = 100, packetId = 10 };

            //ArraySegment<byte> openSegment = SendBufferHelper.Open(4096);
            //byte[] buffer = BitConverter.GetBytes(packet.size);
            //byte[] buffer2 = BitConverter.GetBytes(packet.packetId);
            //Array.Copy(buffer, 0, openSegment.Array, openSegment.Offset, buffer.Length);
            //Array.Copy(buffer2, 0, openSegment.Array, openSegment.Offset + buffer.Length, buffer2.Length);
            //ArraySegment<byte> sendBuff = SendBufferHelper.Close(buffer.Length + buffer2.Length);

            // 100๋ช…
            // 1 -> ์ด๋™ํŒจํ‚ท์ด 100๋ช…
            // 100 -> ์ด๋™ํŒจํ‚ท์ด 100 * 100 = 1๋งŒ
            //Send(sendBuff);
            Thread.Sleep(5000);
            Disconnect();
        }

        public override void OnRecvPacket(ArraySegment<byte> buffer)
        {
            ushort count = 0;

            ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
            count += 2;
            ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + count);
            count += 2;

            switch ((PacketID)id)
            {
                case PacketID.PlayerInfoReq:
                    {
                        PlayerInfoReq p = new PlayerInfoReq();
                        p.Read(buffer);
                        Console.WriteLine($"PlayerInfoReq {p.playerId} {p.name}");

                        foreach (PlayerInfoReq.SkillInfo skill in p.skills)
                        {
                            Console.WriteLine($"skill ({skill.id}) ({skill.level}) ({skill.duration})");
                        }
                    }
                    break;
            }

            Console.WriteLine($"RecvPacketId {id}, Size {size}");
        }

        public override void OnDisconnected(EndPoint endPoint)
        {
            Console.WriteLine($"OnDisconnected : {endPoint}");
        }

        public override void OnSend(int numOfBytes)
        {
            Console.WriteLine($"Transferred bytes : {numOfBytes}");
        }
    }
} 

ํƒœ๊ทธ:

์นดํ…Œ๊ณ ๋ฆฌ:

์—…๋ฐ์ดํŠธ: