개인공부) 서버실습(30) - PacketSession(2)

Justin·2022년 6월 22일
0

서버공부

목록 보기
29/45

✅ 지난시간

간략하게 Send 처리를 하던 부분을 조금 더 효율적으로 개선하는 작업을 배웠다. 오늘도 이어서 더 개선할 수 있는 부분을 찾아 개선할 것이다.

📲 직렬화

❗ 직렬화 사용이유

string이나 list 같은 타입이 추가 될 경우 단순하게 숫자 넘겨주는 방식으로는 진행이 어려워 압축하여 보내는개념

💠 코드 정리

Class 추가
DummyClient -> Server Session
Server -> Client Session Class

❓ 왜 클라이언트에서 사용하는 이름이 서버 세션인가?

세션은 대리자와 유사하게 생각하면 된다. 클라에서 서버와 연결됐을 때 보내주는 대리인이 server session이라 생각하면 된다. 즉, 서버와 통신을 하는 친구이기에 이런 네이밍을 해주었다.

❓❔ 왜 Session을 나누는가?

지난시간 까지 Game Session으로 통일하던
Session을 나누는 이유 지난 번 이유 처럼 나중에는 여러 개의 Session이 생길 수 있기에 구분을 더 잘 하기 위해서

🖥 Server Session

실제 데이터를 넘기는 것 같이 처리하기 위해 이름은 Player 타입으로 지어주었다. PacketID Class 에서는 패킷의 타입을 구분하기 위해 enum 으로 작성한다.

PacketID 는 추후 유저의 이동의 관련한 패킷인지, 스킬인지 등을 구분 하는 방식으로 사용될 수 있다.

    class PlayerInforReq : Packet
    {
        public long playerId; // 8바이트
    }

    class PlayerInforOk : Packet
    {
        public int hp;
        public int attack;
    }

    public enum PacketID
    {
        PlayerInforReq = 1,
        PlayerInforOk = 2,
    }

이제 기존에 연결이 된 뒤(OnConneted 호출 시) 버퍼를 보내던 부분을 개선하고자한다. packet을 받아오던 클래스 타입을 변경하고 size는 나중에 추가한뒤, packetId 값만 1001로 넣어준다.

이전 작업 코드

기존에는 Array.Copy()시에 offset 값을 계속 더해주는 방식을 사용하였다. 하드 코딩으로 진행하면 나중에 보기가 어렵기에 ushort 타입의 변수를 선언하여 처리한다.

ushort count = 0;
Array.Copy(size, 0, openSeg.Array, openSeg.Offset + count, size.Length);
count += 2; // size가 2바이트
Array.Copy(packetId, 0, openSeg.Array, openSeg.Offset + count, packetId.Length);
count += 2; //  packetId가 2바이트
Array.Copy(playerId, 0, openSeg.Array, openSeg.Offset + count, playerId.Length);
count += 8; // long이 8바이트
  • ArrayCopy() 방식은 openSeg에 직접 넣는 방식으로 아래에서 변경되기에 count 추가 개념만 이해하고 지워주자

🚝 BitConverter.GetBytes() -> BitConverter.TryWriteBytes()

기존 사용하던 GetBytes()는 byte[] 로 받기 때문에 기껏 ushort를 사용해 2바이트로 아껴주었는데, 다시 byte[]로 변환하면서 동적으로 할당하기 때문에 사용할 수록 비효율적이다.

BitConverter.TryWriteBytes()
byte를 바로 write 해주고, 사이즈를 비교하여 성공, 실패를 bool 값으로 뱉어준다. 인자로 Span타입을 받는데 이거는 ArraySegment와 유사하다고 보면된다. -> 관련 강사님 답변

ArraySegment<byte> openSeg =
SendBufferHelper.Open(4096);

//사이즈 측정 시 오류가 나는지 체크하기 위함
bool success = true; 

success &= BitConverter.TryWriteBytes(new
Span<byte>(openSeg.Array, openSeg.Offset, openSeg.Count), count);

위와 같이 Span 타입으로 담아 사용하면 된다. byte 배열에 담는게 아닌, 처음 사이즈 정할 때 사용했던 openSeg에 담아주며 사용한다.

openSeg(유효범위)보다 count 값이 큰 경우는 담을 수 없기에 false를 뱉어줄 것이다.

bool &(And) 연산
Success가 기존에 true 이면서 TryWriteBytes()가 뱉어주는 값도 true 일 때만 받아주며 오류가 났을 경우 다음 작업도 진행되지 않게 하기 위함

이어서 packetId, playerId 값들도 이제는 TryWriteBytes 넘겨준다.

count를 - 하는 이유
openSeg.Count가 유효범위를 나타내기에 TryWriteBytes로 사용한 만큼(count)은 빼주어야함

처음 size를 넘겨 주던 작업은 모든 작업이 마친 후에야 정확하게 얼마나 썻는지 알 수 있기에 가장 하단에 미리 계산된 count 함수로 보내준다.)

보낼 때 sucees가 true 즉, 실패가 없었을 때만 보내주는 방식으로 오류를 차단한다. 시스템상 오류, 유저가 악의적읜 이유로 패킷을 이상한 걸 보낼 수 있기에 이런 예외처리들은 필요하다.


😎 Client Session

이제 서버에서 패킷을 받는 쪽 작업을 해주면 이곳은 비교적 간단하다.

  • Server Session과 동일하게 Class들은(PlayerInforReq와 같은) 다 가져와야한다
  • count 사용 방식도 동일하다.

⌨ Switch를 통한 PacketID 구분

앞서 말한대로 PakcetID에 따라 그 패킷을 처리하는 방식은 달라질테니 Switch를 통해 구분할 수 있도록 구분한다.

이렇게 해주면 playerId 값과, 보내는 쪽에서ushort(2byte) 두개, long(8byte) 하나해서 총 12 바이트를 넘겨주었고, Recv에서도 size 12를 잘 받은걸 확인할 수 있다.


여러 방법을 배우며 유니티에서 작동하지 않을 케이스에 대비하고 있다.

지난 번 유니티에서 Save & Load 기능을 제작할 당시에 Serialization 작업을 해서 데이터를 주고 받은 경험이 있어 개념 자체는 알고 있으나, 다른 방법들을 배울 수 있어 나중에 이 부분을 더 효율적으로 개선해봐야겠다.

profile
인디 게임을 만들며 공부하고 있습니다.

0개의 댓글