Java Struct를 이용한 데이터 통신

 C 혹은 C++를 사용하여 구현된 시스템 간의 데이터 통신을 구현할 때, 통신 프로토콜을 struct로 정의하는 방법이 많이 사용된다. 수신 측에서는 바이트 순서(endian)를 고려하여, 수신한 데이터를 미리 정의된 struct 변수에 그대로 할당하기만 하면 된다. 하지만 송신 혹은 수신 측을 C가 아닌 언어로 구현해야 할 때는 약간의 수고가 따른다. 보통은 byte 배열을 사용해 미리 정의된 struct와 동일한 메모리 크기를 할당한 후, struct의 각 필드에 따라 일일히 데이터를 파싱해주어야 한다. 이러한 방법은 구현 시 실수가 발생할 가능성이 높고 자료구조의 수정이나 디버깅도 어렵다.

Javolution


 Javolution은 Java를 실시간 시스템에 적합하도록 지원하기 위해 고성능의 클래스와 유틸리티들을 제공해 주는 라이브러리이다. Java 표준 라이브러리에서 제공하는 StringBuilder, ArrayList, HashMap 등의 클래스는 최초 생성 시 지정된 capacity를 재조정하는데 큰 비용이 따르는데, Javolution에서는 이를 개선한 TextBuilder, FastTable, FastMap 클래스 등을 제공한다. 또한 C/C++과의 상호연결을 위한 Struct, Union 클래스도 제공한다. 본 글에서는 Javolution에서 제공하는 Struct와 Union을 이용하여 Java 환경에서 C로 정의된 struct 및 union과 같은 자료구조를 쉽게 구성할 수 있는 방법에 대해 소개한다. 보다 자세한 내용은 다음 웹페이지를 참조하기 바란다.
http://javolution.org/

Struct 클래스


 C/C++과는 다르게, Java 객체의 메모리 배치는 컴파일러에 의해 결정되지 않는다. 메모리 상의 객체들의 배치는 실행 시간(run time)에 이루어지며 인터프리터(또는 Just-In-Time 컴파일러)에 의해 결정된다. 이 방식은 Java에서 객체를 동적으로 로딩하거나 바인딩할 수 있게 해주지만, C/C++ 코드와 직접적으로 인터페이스 하기에는 어려운 점이 있다.

 Struct 클래스는 C/C++의 struct를 작성하는 것과 같은 방법으로 자료구조를 구성하고, Java 클래스와 C/C++ struct 간에 데이터를 주고받을 수 있게 해준다. Struct 클래스 인스턴스의 메모리 레이아웃은 Struct 클래스를 상속해 정의한 멤버 변수의 순서에 따라 정의되고, C/C++ struct와 동일한 메모리 크기를 할당하게 된다. 다음의 C의 struct 예제를 보자.

  1. enum Gender{ MALE, FEMALE };
  2. struct Date {
  3.    unsigned short year;
  4.    unsigned char month;
  5.    unsigned char day;
  6. };
  7. struct Student {
  8.    enum Gender gender;
  9.    char name[64];
  10.    struct Date birth;
  11.    float grades[10];
  12.    Student* next;
  13. };

 아래는 Struct 클래스를 이용해 위와 동일한 자료구조를 Java로 구현한 것이다. 다른 Struct 클래스를 멤버로 사용하는 경우 inner() 메소드를 사용하는 것과 배열을 사용하는 경우 array() 메소드를 사용하여 초기화 하는 것에 주의한다.

  1. import javolution.io.Struct;
  2. public enum Gender {
  3.    MALE, FEMALE
  4. };
  5. public static class Date extends Struct {
  6.    public final Unsigned16 year = new Unsigned16();
  7.    public final Unsigned8 month = new Unsigned8();
  8.    public final Unsigned8 day = new Unsigned8();
  9. }
  10. public static class Student extends Struct {
  11.    public final Enum32 gender = new Enum32(Gender.values());
  12.    public final UTF8String name = new UTF8String(64);
  13.    public final Date birth = inner(new Date());
  14.    public final Float32[] grades = array(new Float32[10]);
  15.    public final Reference32 next = new Reference32();
  16. }

 Struct 클래스의 멤버 변수는 아래와 같이 접근한다.

  1. Student student = new Student();
  2. student.gender.set(Gender.MALE);
  3. student.name.set("Meg Anero"); // Null로 끝남(C compatible)
  4. int age = 2003 - student.birth.year.get();
  5. student.grades[2].set(12.5f);
  6. student = student.next.get();

packed 속성 및 바이트 순서


 struct의 packed 속성은 아래와 같이 isPacked() 메소드를 오버라이드 하여 설정한다.

  1. @Override
  2. public boolean isPacked() {
  3.     return true; // 혹은 false
  4. }

 바이트 순서는 아래와 같이 byteOrder() 메소드를 오버라이드 하여 설정한다.

  1. @Override
  2. public ByteOrder byteOrder() {
  3.     return ByteOrder.LITTLE_ENDIAN; // 혹은 ByteOrder.BIG_ENDIAN
  4. }

Union 클래스


 union도 Struct 클래스가 아닌 Union 클래스를 상속 받는 것을 제외하고는 대부분 동일한 방법으로 구현한다.

  1. union Number {
  2.     int asInt;
  3.     float asFloat;
  4.     char asString[12];
  5. };

 Union 클래스 구현:

  1. import javolution.io.Union;
  2. public class Number extends Union {
  3.     Signed32 asInt = new Signed32();
  4.     Float32 asFloat = new Float32();
  5.     Utf8String asString = new Utf8String(12);
  6. }

 멤버 변수 접근:

  1. Number num = new Number();
  2. num.asInt.set(23);
  3. num.asString.set("23");
  4. float f = num.asFloat.get();

References

댓글

이 블로그의 인기 게시물

Redmine 서버 백업 및 복원

Emacs Windows 환경에서 한영키 입력 전환 문제