C#

직렬화(Serialization) 2

monstro 2025. 1. 1. 17:54
728x90
반응형

이번에는 기존의 패킷에서 Session들에서 수행하던

Read와 Write를 패킷에서 수행할 수 있도록 변경해보겠습니다.

 

공통적으로 패킷의 구조를 다음과 같이 변경하였습니다.

차후에 패킷의 클래스를 따로 선언하여 클라이언트와 서버가 갖게끔 구성하도록 수정하겠습니다.

 

1) Server와 Client 모두가 공통적으로 갖는 패킷 

// 패킷의 인자를 설정하는 경우, 최대한 용량을 줄여주는 것이 좋음
public abstract class Packet
{
    // 패킷의 용량
    public ushort _packetSize;
    // 패킷의 아이디
    public ushort _packetID;

    // 패킷을 읽고, 패킷에 쓰기 위한 함수
    public abstract void Read(ArraySegment<byte> segment);
    public abstract ArraySegment<byte> Write();
}

// PlayerInfoRequire의 멤버는 _packetSize, _packetID, _playerId = 12 바이트
class PlayerInfoRequire : Packet
{
    public long _playerID;
	
    // 생성자에서 PlayerInfoRequire 패킷의 PacketID를 설정함
    public PlayerInfoRequire()
    { 
        this._packetID = (ushort)PacketID.PlayerInfoRequire;
    }

    // 패킷을 읽는(=역직렬화) 로직은 다음과 같이 동작함
    // PlayerID의 경우, 읽기 전용으로 만들어 외부에서 패킷을 덮어씌우는 경우를 막음
    public override void Read(ArraySegment<byte> segment)
    {
        ushort count = 0;

        count += 2;
        count += 2;
        this._playerID = BitConverter.ToInt64(new ReadOnlySpan<byte>(segment.Array, segment.Offset + count, segment.Count - count));
        count += 8;
    }

    // 패킷을 쓰는(=직렬화) 로직은 다음과 같이 동작함
    // 기존의 방식 대신 this 포인터를 사용하여 프로퍼티로 설정함
    public override ArraySegment<byte> Write()
    {
        ArraySegment<byte> segment = SendBufferHelper.Open(4096);

        bool success = true;
        ushort offset = 0;

        success &= BitConverter.TryWriteBytes(new Span<byte>(segment.Array, segment.Offset, segment.Count), this._packetSize);
        offset += 2;
        success &= BitConverter.TryWriteBytes(new Span<byte>(segment.Array, segment.Offset + offset, segment.Count - offset), this._packetID);
        offset += 2;
        success &= BitConverter.TryWriteBytes(new Span<byte>(segment.Array, segment.Offset + offset, segment.Count - offset), this._playerID);
        offset += 8;
        success &= BitConverter.TryWriteBytes(new Span<byte>(segment.Array, segment.Offset, segment.Count), (ushort)offset);

        if (success == false)
            return null;

        return SendBufferHelper.Close(offset);
    }
}

public enum PacketID
{
    PlayerInfoRequire = 1,
    PlayerInfo = 2,
}

 

패킷의 구조를 위와 같이 수정하였습니다.

따라서 Session 별로 쓰기와 읽기를 수행하지 않고 

패킷을 생성하여 생성된 패킷의 인스턴스에서 읽기와 쓰기를 수행할 수 있습니다.

 

바뀐 구조에 맞춰 Client와 Server 코드를 수정해보았습니다.

 

2) 클라이언트 - ServerSession

class ServerSession : Session
{
    // 연결되는 경우 호출
    public override void OnConnected(EndPoint endPoint)
    {
        Console.WriteLine($"[Connect from] : {endPoint}");

        PlayerInfoRequire packet = new PlayerInfoRequire()  { _playerID = 1001 };

        // 서버로 전송
        {
            ArraySegment<byte> segment = packet.Write();
            if (segment != null)
                Send(segment);
        }
    }

   .
   .
   .
}

 

새로운 PlayerInfoRequire 패킷의 인스턴스를 생성하고,

생성된 패킷 인스턴스로부터 Write 메서드를 수행하여 직렬화를 한뒤

Send를 통해 해당 패킷을 서버로 전송합니다.

 

3) 서버 - ClientSession

class ClientSession : PacketSession
{
	.
	.
	.
    
    public override void OnRecvPacket(ArraySegment<byte> buffer)
    {
        ushort count = 0;
		
        // buffer로 받은 패킷을 가져와 헤더를 가져오고
        ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
        count += 2;
        ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + count);
        count += 2;

        // 패킷의 ID를 가져와서
        switch ((PacketID)id)
        {
            // PlayerInfoRequire 패킷인 경우 다음의 로직을 수행함
            case PacketID.PlayerInfoRequire:
                {
                    PlayerInfoRequire playerInfoRequire = new PlayerInfoRequire();
                    playerInfoRequire.Read(buffer);
                    Console.WriteLine($"PlayerInfoReq : {playerInfoRequire._playerID}");
                }
                break;
        }

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

 

PlayerInfoRequire 패킷에 대한 역직렬화를 

PlayerInfoRequire의 메서드인 Read를 통해 수행하는 것을 볼 수 있습니다.

 

최종 수행 결과는 다음과 같습니다.

728x90
반응형

'C#' 카테고리의 다른 글

직렬화(Serialization) 3  (0) 2025.01.06
UTF-8과 UTF-16  (0) 2025.01.06
직렬화(Serialization) 1  (1) 2025.01.01
PacketSession  (0) 2024.12.31
RecvBuffer와 SendBuffer  (0) 2024.12.30