serialization, deserialization. 직렬화와 역직렬화 작업을 간단하게 실험해보며 현재 부족한 부분의 코드를 개선하는 작업을 하였다.
지난시간에 열심히 개선해준 OnConnected에서 buffer를 날려준 부분을 더 사용하기 편하게 PlayerInforReq의 함수로 만들어 줄 것이다.
사용하지 않는 PlayerInforOk는 지워주고, 뒤에서 읽는 부분도 사용성을 높이기 위해 처리할 것이기에 Read() 그리고 지금 작업할 Write() 함수를 제작한다.
// 가장 상위 Class에서 abstract 방식으로 선언해둔다.
public abstract class Packet
{
// 크게 사용성이 없기에 제거해도 괜찮다. size, packetId
public ushort size;
public ushort packetId;
// send, Recv 생성을 하기 위한 클래스 생성
public abstract ArraySegment<byte> Write();
public abstract void Read(ArraySegment<byte> s);
}
// Packet에 제작 된 함수를 자식에서 완성 시켜 사용한다.
class PlayerInforReq : Packet
{
public long playerId; // 8바이트
public override void Read(ArraySegment<byte> s)
{
}
public override ArraySegment<byte> Write()
{
}
크게 복잡한 내용은 없고, 기존 OnConnected에서 동작하던 코드 들을 이쪽으로 이사 시켜준다. pakcId도 이제 바로 받을 수 있기에 수정해준다. 이 곳에 packetId를 구분하기 위해 this 키워드 사용한다.
return값 자체가 ArraySegement이기에 Close() 해줄 때도 ArraySegement 변수 없이 바로 닫아주기만 하면 된다. 추가로 OnConnected에서 직접 넣던 packetId도 여기서 넣는다.
public PlayerInforReq()
{
this.packetId = (ushort)PacketID.PlayerInforReq;
}
이 부분은 Send를 통해 받아준 서버의 Recv 부분을 가져와 개선하면 된다.
저 두곳에 있는 코드를 가져와 아까 만들어둔 Read()에 담아준다.
여기서 ushort size는 해당 작업 전에 추출해야하기에 지금 단계에서는 해줄 필요가 없고
packetId도 이미 Switch 문에서 걸러주고 있기에 packetId를 출력할 필요가 없어 모두 제거해준다.
long playerId = BitConverter.ToInt64(buffer.Array,
buffer.Offset + count) ;
그렇다면 이제 이 부분 밖에 남지 않게 되는데 Read에서는 바로 요 부분을 수정해주어야 한다.
클라에서 패킷을 보낼 때는 잘못된 정보가 올 수 있다고 생각한다.
이 부분은 클라에서 패킷 전송 처리 하는 부분인데, 기존에는 count 변수를 통해 12라는 값이 전송되어야 하지만 임의로 4바이트를 보내본다.
클라가 예상한 것과 다른 값을 보낸다면 오류가 나야할 것 같지만, size 값만 달라지고 실행은 잘 된다. 이렇게 처리되면 악의적인 유저가 패킷을 꼬아서 보낼경우 열심히 만들어 놓은 패킷 시스템이 꼬이면서 큰 문제가 발생할 수 있다.
public override void Read(ArraySegment<byte> s)
{
ushort count = 0;
count += 2;
count += 2;
this.playerId = BitConverter.ToInt64(new
ReadOnlySpan<byte>(s.Array, s.Offset + count,
s.Count - count));
count += 8;
}
여기서 BitConverter.ToInt64
의 오버로딩된 다른 기능을 활용한다. ReadOnlySpan<>을 통해서 값을 불러오고 만약 값이 이상하다면 오류를 출력해줄 것이다.
이렇게 클라에서 보낸 정보를 무조건 Read 하는 것이 아니라, 크기가 정확한지 안정성을 위해 패킷의 사이즈를 한 번 더 체크해야한다.
참고로 패킷을 Recv 하던 부분은 playerinforReq에서 함수호출 하는 방식으로 변경하였다.
참고로 위에 코드들이 잘 작동하기 위해서는 Packet, PlayerInforReq를 모두 Client, ServerSession 둘다 가지고 있어야한다.
-> 이는 추후에 라이브러리 형태로 개선할 수 있다.
이렇게 조금 더 안정성 높은 코드로 개선하는 간단한 작업을 실행하였다. 더 상세하게 들어가는 것 보다는 이렇게 예시를 통해 빠르게 배우고, 추후에 직접 만들고 운영해보며 개선하는 작업을 해보고 싶다.