PacketByte Support 라이브러리

안녕하세요.
개인적으로 쓸 일이 있어서 만들어보았는데
Nuget Library에 생각보다 별로 없는거 같아서 올려보았습니다.

Nuget 패키지명… 이름 오타여서 어떻게해야하나 고민했지만…
이것 또한 컨셉이라 생각하고 냅두기로 하였습니다.

다음은 패키지에서 제공하는 기능에 대한 간략한 설명입니다.

Chain형식의 Append 방식 기능 제공

PacketWriter writer = new PacketWriter()
.Append(0x01)
.Append(1)
.Append(1, false)
.Append(“ABC”)
.Append(“abc”);

var builder = new PacketBuilder ()
               .Append(0x40)
               .Append(0x41)
               .Append(0x42)
               .Append(0x43)
               .Append(0x44)
               .Append(0x45)
               .Build();

확장메서드 제공

형식이 다른 타입의 경우 Class로 생성하여 처리할 것을 권장

byte[] abc = new byte[]{0x01, 0x02};
abc = abc.Append(0x01);
abc = abc.Append(1);            // LITTLE ENDIAN
abc = abc.Append(1, false);  // BIG ENDIAN
abc = abc.Append("ABC");
abc = abc.Append("abc");

서로 다른 Byte Array가 큰 덩어리 일때에만 사용할 것을 권장

byte[] abc = new byte[]{0x01, 0x02};
abc = abc.Append(new byte[]
                  {
                  0x03,
                  0x03,
                  0x03,
                  0x03,
                  0x03,
                  0x03,
                  0x03,
                  0x03,
                  });

Display용 string(string type)

var writer = new PacketWriter ()
.Append (0x40)
.Append (0x41)
.Append (0x42)
.Append (0x43)
.Append (0x44)
.Append (0x45);
var bytes = writer.GetBytes();
Console.WriteLine (bytes.Display ());
Console.WriteLine (bytes.DisplayAscii ());

var builder = new PacketBuilder ()
               .Append (0x40)
               .Append (0x41)
               .Append (0x42)
               .Append (0x43)
               .Append (0x44)
               .Append (0x45)
               .Build();

Console.WriteLine (builder.Display ());
Console.WriteLine (builder.DisplayAscii ());
// output

// 404142434445
// @ABCDE

Array 또는 List의 byte타입 삽입

List<byte> testByte = new List<byte>(){0x51, 0x52, 0x53};

summaryByts = testByte.Append(new List<byte>
                                 {
                                   0x54,
                                   0x55
                                 });
var writer = new PacketWriter ();
writer.Append(new List<byte>()
              {
              0x40,
              0x41,
              0x42,
              0x43,
              0x44,
              0x45,
              });
/* and
 writer.Append(new byte[]
              {
              0x40,
              0x41,
              0x42,
              0x43,
              0x44,
              0x45,
              });
*/

Class Type Insert

public class Test2Packet
{
    public int Value;
    [ByteSize (3)]
    public string Value1;
    [ByteSize (3)]
    public string Value2;
}

var writer = new PacketWriter ()
                        .Append(new Test2Packet()
                              { 
                                    Value = 1,
                                    Value1 = "abc",
                                    Value2 = "Abc"
                              });

bytearray - class (Serialization, Deserialization)

public class Test2Packet
{
    public int Value;
    [ByteSize (3)]
    public string Value1;
    [ByteSize (3)]
    public string Value2;
    [ByteSize(4)]
    public byte[] abc;            
    public byte[] efg = new byte[5];
    public List<byte> qqq= new List<byte>(5);
    public List<byte> qqq1= new List<byte>();    // ByteSize Attribute 또는 Capcacity값 설정 하지 않았기 때문에 삽입하지 않음
}

[Fact]
public void TestDeserializeObject()
{
    var test = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x61, 0x62,0x63,0x41,0x42,0x43, 0x01, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x41, 0x42, 0x43, 0x01, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63, 0x41, 0x42, 0x43 };
    var aaa = PacketParse.DeserializeObject<Test2Packet> (test);

    var abc  = PacketParse.Serialization (aaa);
}

PacketByte에 필요한 기능이다 생각이 드는것들은 조금씩 조금씩 업데이트 예정이며,
현재 개발해놓은 부분에서 버그가 보인다면 바로 말씀 부탁드릴게요! 바로바로 수정하겠습니다

16 Likes

이것은 어디에, 어떨때 쓰는 것인가요?

3 Likes

소켓 통신 또는 시리얼 통신 할때 보통 대개는 Byte 통신을 많이 하게 되는데

Byte 송수신을 할 때
저 위 라이브러리처럼 비슷하게 직접 구현을 하거나
byte sendByte = new byte{0x01 …0xFF}; 255자의 바이트 Array 구성을
코드에 그대로 넣어 관리하는 경우도 있습니다.

그러다보면 byte를 특정 배열부터 배열까지의 값을 sendbyte[n] ~ sendbyte[n+@]까지의 값을 찾아내며 봐야하기 때문에 디버깅하기도 힘듭니다.

하지만 아래와 같은 패킷 구조를 가진 데이터 통신 패킷이 있다고 했을때

위 byteArray 데이터를 아래 클래스 타입으로 캐스팅함으로써
좀 더 읽기쉬운 코드로 만들 수 가 있습니다.

public class ethernetDataFormat
{
     [ByteSize (8)]
     public  byte[] Preamble;
     [ByteSize (6)]
     public  byte[] MAC목적지;
     [ByteSize (6)]
     public  byte[] MAC소스;
     [ByteSize (2)]
     public  byte[] Ethertype;
     [ByteSize (46)] // 또는 [ByteSize(1500)]
     public  byte[] Payload;
     [ByteSize (12)]
     public  byte[] CRC32;
}

최근

이 게시글과도 관련이 있습니다.

11 Likes

혹시 System.IO.Pipelines로는 말씀하신 시나리오의 달성에 어려움이 있는 것인가요?

4 Likes
async Task ProcessLinesAsync(NetworkStream stream)
{
    var buffer = new byte[1024];
    await stream.ReadAsync(buffer, 0, buffer.Length);

    // Process a single line from the buffer
    ProcessLine(buffer);
}

이 부분을 말씀하신건가요??

4 Likes

BinaryWriter 나 ArrayConcat 보다 훨씬더 깔끔하고 사용성이 좋은것 같습니다. 클래스에서 Serialize 개념으로 사용 할때도 유용하겠네요.
저수준 통신이나 파일저장이나 deepcopy 용도로도 유용할 것 같습니다.
감사합니다.

5 Likes

감사합니다 :slight_smile:
좀 더 서브적인 기능들이 아이디어 떠오를때마다 넣어보겠습니다!

3 Likes

그 코드는 Pipelines 사용 코드가 아닌 C#에서 기본적인 네트워크 데이터 수신 처리 코드 입니다.

소개해 주신 PacketByte Append 라이브러리는 말씀 하신 것 처럼
RPC 통신이 아닌
Raw TCP/IP 소켓 통신시 약속된 패킷 규약 구조를 정의해서 Byte통신시 사용 하면 편리할 것 같습니다.
(MessagePack 처럼)

더불어 위에서 언급된 System.IO.Pipelines 는 Read / Write 처리를 파이프라인을 통해 추가적인 버퍼할당을 피해서 고성능 IO 처리를 쉽게 처리해 주는 라이브러리 입니다.
※ asp net core의 Kestrel 이 사용되는 방식 입니다.


System.IO.Pipelines 는 고성능적인 IO 처리를 하는 라이브러리 이고,
소개해주신 PacketByte Append 는 Raw방식의 소켓 통신 사용시 효율적인 Serialization / Deserialization 처리를 하는 것으로 이 둘이 대체 될만한 것은 아닌 것 같습니다.

7 Likes

아 아니요.

제가 상황을 다 이해하지 못한 거 같아서 질문드리는 것 같네요.

순차적으로 데이터를 만들어서 보내기에 편한 어떤 Packet Entity를 만드는 라이브러리 같이 느껴져서 그런거라면 파이프라인으로 되는거아닌가? 라고 생각하여 질문드린 것입니다.

4 Likes

시리얼 장비(영수증프린터 등) 에 활용하기 아주 좋을거 같습니다.

공유 감사합니다!

5 Likes

@이광석 님 Append를 할 때마다 byte[]를 생성하고 데이터를 복사하는 것 보다 Build하는 시점에서 일괄 생성하는 것은 어떨까요? Append()를 할 때마다 추가할 데이터의 길이를 확인할 수 있어서 최종 Build() 등의 메서드로 패킷을 만드는 느낌으로요

6 Likes

아 좋은생각인거같아요!
다음 업데이트 때 고려해보겠습니다!


우선 Append의 확장메서드는 2 그룹 이상의 Byte array는 권장하지 않는 개념으로 가야할 것 같네요.

@dimohy 님께서 말씀해주신 방법대로
기존 PacketWriter에서의 Append 개념을 없애고 PacketBuilder라는 클래스로 이름을 변경과 동시에 Append개념을 없애고자합니다. (List로 쌓아놔도…ㅎㅎ)
현재 Add라는 메서드와 Build라는 메서드만 존재하는데
다른 분들께서 Add 대신 좋은 메서드명 있으면 추천 부탁드리겠습니다.

 public partial class PacketBuilder
 {
     private List<byte> packetData = new List<byte> ();

     public PacketBuilder Add(byte data)
     {
         packetData.Add (data);

         return this;
     }

     public PacketBuilder Add(byte[] data)
     {
         packetData.AddRange(data);

         return this;
     }

     public PacketBuilder Add(string ascii)
     {
         packetData.AddRange (ByteConverter.GetByte (ascii));
         return this;
     }

     public PacketBuilder Add(int intByte, Endian endian = Endian.BIG)
     {
         packetData.AddRange (ByteConverter.GetByte (intByte, endian));
         return this;
     }

     public PacketBuilder Add(long longByte, Endian endian = Endian.BIG)
     {
         packetData.AddRange (ByteConverter.GetByte (longByte, endian));
         return this;
     }

     public PacketBuilder Add(short shortByte, Endian endian = Endian.BIG)
     {
         packetData.AddRange (ByteConverter.GetByte (shortByte, endian));
         return this;
     }

     public PacketBuilder Add<TSource>(TSource AppenClass) where TSource : class
     {
         packetData.AddRange (PacketParse.Serialization (AppenClass));
         return this;
     }

     public byte[] Build()
     {
         return this.packetData.ToArray ();
     }
 }

ps. 애시당초 PacketWriter는 향후 CheckSum 기능도 넣을 예정이였기 때문에 Writer에 본분을 계승받아 PacketBuilder의 향후 기능으로는 Checksum 기능들이 추가 예정입니다!

7 Likes

최대한 BCL 라이브러리 사용 경험과 유사하게

StringBuilder 의 AppendToString 느낌 그대로

AppendToArray 로 가면 어떨까 생각해봅니다.

6 Likes

저는 개인적으로 패킷 빌드할 때 그냥 대충 확장 메서드 선언하고 쓰고 있답니다.

public static class SingleByteConcatExtention
{
    public static IEnumerable<byte> Append(
        this IEnumerable<byte> enumerable, byte value)
    {
        foreach (var item in enumerable) yield return item;
        yield return value;
    }

    public static IEnumerable<byte> Checksum(
        this IEnumerable<byte> enumerable)
    {
        uint sum = 0;
        foreach (var item in enumerable)
        {
            sum += item;
            yield return item;
        }
        yield return (byte)(sum & 0xff);
    }
}

//바이트 열거 만들기
var packet = Enumerable.Empty<byte>()
    .Append(0x01)
    .Concat(Encoding.ASCII.GetBytes("message"))
    .Concat(BitConverter.GetBytes(0.2f))
    .Append(0x03)
    .Checksum();

//바이트 배열로 할당
var cache = packet.ToArray();

Append는 바이트 하나 추가할 때만 쓰고 여러 개는 그냥 Linq의 Concat을 쓴답니다.
IEnumerable과 yield로 적당히 열거 동작 만들고, 필요할 때는 배열로 할당해서 쓰고 있죠. ㅎㅎ

독립된 형태의 라이브러리까지는 만들지 않고 있었는데 그에 더해서 직렬화/역직렬화 기능까지 만드시다니 기대가 됩니다. ^^

6 Likes

감사합니다…!

더 기대될만한 기능들을 만들어보도록하겠습니다!

8 Likes

오…많은 사람들의 반응이 뜨거운 것보니 소켓통신을 하게되면 아무래도 유용한 것 같군요…사용해볼 기회가 있었으면 좋겠군요.

대성하기를 응원합니다

3 Likes

보통 직접 구현하시는 분들이 많으시죠…하핫!

4 Likes

아 저는 저런게 있다 하고 소개해 주신줄 알았는데 ㅋ (난독증이…)

다시 보니 직접 만드신거군요 !

대단하시군여 ! 좋아요 !!! ㅎㅎ

3 Likes

앗 원래 개발자라면 그렇지않습니까 핵심글귀만 쑥 보고 다음문장이 아닌 다다다다다음 문장…

3 Likes

확장메서드의 Append와 다른 개념으로 넣어야 한다는 고민에 Append와 Add 두개를 한참 고민했었는데
@BOBx5 님의 의견과
@Vagabond-K 님 직접 구현하시는 소스에도 Append 표현을 쓰이는걸 보니…

그냥 Append가 낫겠군요… :smile:

ToArray의 메소드는 향후 CRC과 같은 CheckSum을 고려해서 Build라는 메서드 표현이 좋을 거같아요!

추천해주신 Append와 ToArray 중에 Append로 수정하겠습니다!

4 Likes