본문 바로가기
cs공부/네트워크프로그래밍

네트워크프로그래밍 - packetId와 size에 대한 이슈(client의 거짓말, ReadOnlySpan)

by 코딩하는 돼징 2023. 8. 7.
반응형

패킷 자동화 처리 알아보러가기

 

네트워크프로그래밍 - 패킷 자동화 처리 객체지향 방법으로 수행하도록 하기

패킷 자동화 처리 알아보러 가기 네트워크프로그래밍 - 패킷 자동화 처리(TryWriteBytes,c#에서 포인터 사용하는 법) 패킷작업하기 서버, 클라이언트에 이 정보가 다 있어야 한다. class Packet { public sho

code-piggy.tistory.com


packetId를 굳이 패킷에 넣어주어야 하는가 ?

Header를 보면 size와 packetId를 우리가 굳이 직접 사용해야 하는 경우가 거의 없다.

public abstract class Packet
{
    public ushort size;
    public ushort packetId;
}

Write는 우리가 open및 close하면서 우리가 다 컨트롤 하는 부분이므로 문제가 없다.

하지만 Read에서는 우리가 Read를 하는 부분은 Client에서 Sever쪽으로 packet을 보내는데 이를 까보면서 작업 한다. 그러므로 Read는 size를 넘겨준게 과연 실제 packet과 일치하는지 판별하는데 어려운점이 있다.

ClientSession.cs

public override void OnReceivePacket(ArraySegment<byte> buffer)
{
    ushort count = 0;
    // size가 과연 진짜일까?
    ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
}

왜 이런일이 발생할까?

packetHeader에 있는 정보는 믿을 수 없는 정보 일 수 있다. 그러므로 항상 참고만 해야 한다.

 

Client의 거짓말

서버를 만들때는 항상 Client가 거짓말을 한 다고 가정하고 만드는게 좋다. 클라이언트는 네트워크 통신시 패킷의 내용을 조작할 수 있다. 


패킷의 size를 변경하여 서버로 전송하는 예시

01 기존 12byte를 보내는 상황

count += 2;        
success &= BitConverter.TryWriteBytes(new Span<byte>(openSegment.Array, openSegment.Offset + count, openSegment.Count - count), this.packetId);
count += 2;
success &= BitConverter.TryWriteBytes(new Span<byte>(openSegment.Array, openSegment.Offset + count, openSegment.Count - count), this.playerId);
count += 8;
success &= BitConverter.TryWriteBytes(new Span<byte>(openSegment.Array, openSegment.Offset, openSegment.Count), count);

02 4byte라고 거짓말 하는 상황

count += 2;
success &= BitConverter.TryWriteBytes(new Span<byte>(openSegment.Array, openSegment.Offset + count, openSegment.Count - count), this.packetId);
count += 2;
success &= BitConverter.TryWriteBytes(new Span<byte>(openSegment.Array, openSegment.Offset + count, openSegment.Count - count), this.playerId);
count += 8;
success &= BitConverter.TryWriteBytes(new Span<byte>(openSegment.Array, openSegment.Offset, openSegment.Count), (ushort)4);

03 결과

아까와 마찬가지로 1001이 제대로 간 것을 확인할 수 있다.

어떻게 제대로 간 것 일까?

12byte가 정상적으로 와야하는데 우리가 4byte만 유효범위라고 Read를 호출한다. 그렇다고 해서 뒷부분이 메모리상에서 없는 것은 아니다. 그러므로 데이터가 없는 것은 아니다. 


이를 방지하는 방법

사이즈를 넘는지 안넘는지 체크

01 기존 코드

byte배열에서 packetId를 직접 읽어 온다.

this.packetId = BitConverter.ToUInt16(openSegment.Array, openSegment.Offset + count);

02 수정 코드

openSegment.Count -  count(현재 처리된 바이트)가 0보다 작다면 예외처리가 발생한다.

this.packetId = BitConverter.ToUInt16(new ReadOnlySpan<byte>(openSegment.Array, openSegment.Offset + count, openSegment.Count - count));

ReadOnlySpan

지정한 인덱스에서 시작하는 배열의 특정 개수의 요소를 포함하는 새로운 ReadOnlySpan<T>를 생성

public ReadOnlySpan (T[]? array, int start, int length);

매개변수

array: ReadOnlySpan을 생성할 배열

start : array배열에서 포함할 첫번째 요소 인덱스

length : 포함할 요소의 개수

 

 

 

 

 

 

참고 :  본 내용은 MMORPG  PART4 강의를 수강하여 작성하였습니다.

https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4

 

 

 

 

 

 

 

 

 

반응형

댓글