자바와 C/C++ 간의 데이터 통신

author : Yoon Kyung Koo(yoonforh@yahoo.com)
Copyright (c) 1999-2000 Yoon Kyung Koo, All rights reserved.
자바 프로그램과 C/C++ 프로그램 사이에 데이터 통신(주로 소켓을 사용하겠지요...)을 할 때에는
자바의 객체 스트림(java.io.Serializable or java.io.Externalizable을 인터페이스를 구현하고
ObjectInput/OutputStream 사용)을 사용할 수가 없습니다.
자바의 객체 스트림은 자바에 고유한 형식을 사용하기 때문에 C/C++ 프로그램이 이해할 수 없기
(어렵기?) 때문입니다.
따라서 자바와 C/C++ 프로그램 사이에서 데이터를 주고 받으려면 기본 유형으로 변환하여
주고 받을 수밖에 없습니다.
예를 들어 다음과 같은 필드들로 구성된 자바 클래스를 C/C++ 프로그램과 주고 받는다고
생각해봅시다.

class TestData {
    byte[] bytes = new byte[100] ;
    char c; // assume this char value will not excced one byte (i.e. cannot use Hangul char)
    boolean val;
    String string;
}

대응되는 C/C++ 데이터 구조체를 다음과 같이 생각할 수 있겠습니다.

typedef struct {
    char bytes[100];
    char c; // 물론 이렇게 구현하면 안됩니다. C의 구조체 alignment에 따라
           // 사용되지 않는 몇 바이트가 채워지게 됩니다.
    bool val; // 보통 C++의 bool 유형은 int처럼 4바이트지만 컴파일러와 O/S에 따라 다릅니다.
              // 예를 들어 64비트 머신인 디지털 유닉스 4.0의 g++ 2.95에서 bool은 8바이트를
              // 차지합니다. (같은 기계에서 cxx의 경우 4바이트를 차지합니다.)
    int str_length; // 문자열의 길이
    char * string;
} Object ;

이럴 경우에 발생하는 문제는 여러 가지가 있을 수 있습니다.
1. C/C++에서 structure가 차지하는 각 필드들의 바이트 수...
위의 경우처럼 char가 중간에 혼자 들어가게 되면 3바이트가 덧붙는 경우가 생기게 됩니다.

2. 각 기본 유형들의 데이터 길이...
자바는 고정되어 있지만 C/C++의 경우에는 컴파일러와 O/S 등에 따라 차이가 납니다.
예를 들어 long의 경우 자바는 8바이트이지만 C/C++의 경우 운영 체제에 따라 4바이트와 8바이트가
각각 사용될 수 있습니다.
GNU의 C/C++ 컴파일러를 사용하면 8바이트 유형인 long long를 사용하여 자바의
long에 대응시킬 수 있습니다.
위 예제에서는 bool 유형(보통 4바이트지만 어떤 컴파일러/운영체제에서는 8바이트로 처리),
int 유형(16비트 운영체제에서는 2바이트로 처리), char *와 같은 주소 유형(CPU 종류에 따라
2바이트, 4바이트, 8바이트 등으로 처리) 등이 C/C++ 쪽에서 주의해야 할 유형입니다.

3. short, int, long 등의 바이트 순서
자바는 네트웍 순서(빅 엔디안)로 고정되어 있지만 C/C++의 경우에는 CPU 유형에 따라
달라지므로 반드시 보정해줘야 합니다.
만약 자바쪽에서 리틀 엔디안으로 변환하여 보내려면 htonl/htons 기능을 하는 메소드를
구현하여야 합니다.
 참고 : 자바 기본 유형의 바이트 순서 변환

4. char의 인코딩...
자바는 String을 내부적으로 유니코드를 사용하니까 바이트로 변환하여 C/C++와 주고받아야 합니다.
특히 한글과 같은 2바이트 문자를 사용할 때에는 더욱 중요합니다.
DataOutput 혹은 ObjectOutput의 void writeBytes(String)과 같은 메소드는
무조건 문자를 1바이트로 처리하여 쓰므로 2바이트 문자가 사용될 경우에는 사용하지 말아야 할
메소드입니다.
(String의 length()와 문자열을 바이트열로 변환한 byte[]의 length 값이 다를 수 있다는 건 아시지요?)

다음은 간단한 예제입니다. 간단하게 파일을 사용한 예입니다.
자바 프로그램이 데이터를 파일로 쓰고, C++ 프로그램이 그 파일을 읽는 것이지요.
C++ 프로그램은 위에서 말씀드린 여러 가지 이유로 운영 체제에 따라 수정할 필요가 있습니다.
제가 테스트한 운영체제는 솔라리스 2.6입니다.
컴파일
   javac -encoding EUC_KR FileIOTest.java
   g++ -o tester FileIOTest.C

실행
   java FileIOTest file.dat
   tester file.dat

실행 결과
   다음 파일을 참조하세요...
   result.txt

다음은 간단한 소켓 예제입니다. 서버를 유닉스에서 c로 작성한 프로그램을 사용하고 클라이언트를 자바로 작성합니다.
C 프로그램은 위에서 말씀드린 여러 가지 이유로 운영 체제에 따라 수정할 필요가 있습니다.
제가 테스트한 운영체제는 솔라리스 7입니다.
컴파일
   javac -encoding EUC_KR TestClient.java
   gcc -o test_server test_server.c -lsocket -lnsl

실행
   (솔라리스) test_server 10001 (인자는 포트 번호입니다.)
   (자바 클라이언트) java TestClient hostname 10001 (hostname은 실제 서버가 있는 호스트 이름)

실행 결과
   다음 파일을 참조하세요...
   (서버쪽)server_result.txt
   (클라이언트쪽)client_result.txt

이 페이지는 1999년 9월 10일에 처음 만들어지고 2000년 2월 12일에 최종 갱신되었습니다.
튜토리얼 페이지로 돌아가기
Last modified: Sat Feb 12 15:24:24 2000