프로그래밍/Java,JSP

자바에서 부호없는 데이터(Unsigned data) 다루기

채윤아빠 2012. 11. 7. 23:30
728x90
반응형

서론

네트워크를 통하여 신호 처리를 처리하는 과정에서 값 처리 중에 오류가 계속 발생하였습니다. 네트워크를 통하여 받은 패킷 중에 unsigned char, unsigned short 등의 데이터들이 포함되어 있었는데, 문제는 자바에서는 부호없는(unsigned) 데이터형이 존재하지 않아서 버퍼에서 읽은 데이터를 길이에 맞는 데이터로 처리를 하게되면, 원치않게 음수값이 되어 버려서 문제가 되었습니다.

본 글에서는 자바에는 없는 부호없는(unsigned) 데이터형을 어떻게 처리해야 하는지 그 방안에 대해서 간단하게 설명합니다.

본론

다음은 unsigned byte를 변환하는 과정에서 발생하는 오류와 이를 바로잡는 방법을 보여주는 예제 소스입니다.
import java.nio.ByteBuffer;
 
/**
 * @author hbesthee@naver.com
 *
 */
public class ConvertUnsignedByteToShort
{
	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		System.out.println();
		System.out.println("===== unsigned byte to short (변환 오류 예) ===");
		{
			ByteBuffer objByteBuffer1 = ByteBuffer.allocate(2);
			byte bValue = (byte)0xFA;
			short nShort = bValue; // unsigned byte 0xFA를 입력하였으나, 실제 short에는 -6이 입력됨
			System.out.print("unsigned byte(0xFA) to short : " + nShort + " ==> Hexa : 0x");
			objByteBuffer1.order(ByteOrder.BIG_ENDIAN);
			objByteBuffer1.putShort(nShort);
	        byte[] arrIntbytes = objByteBuffer1.array();
	        for (int i = 0; i < arrIntbytes.length; i++)
				System.out.printf("%02X", arrIntbytes[i]);
		}

	    System.out.println();
		System.out.println("\n===== ByteBuffer의 unsigned byte를 short로 변환(마스킹 사용) ===");
		{
			ByteBuffer objByteBuffer = ByteBuffer.allocate(4);
			objByteBuffer.put((byte)0xFF);
			objByteBuffer.put((byte)0xFE);
			objByteBuffer.put((byte)0xFD);
			objByteBuffer.put((byte)0xFC);
			byte [] arrBuffBytes = objByteBuffer.array();
		    for (int i = 0; i < arrBuffBytes.length; i++)
				System.out.printf("[%02X]", arrBuffBytes[i]);
		    objByteBuffer.position(0);
		    for (int i = 0; i < arrBuffBytes.length; i++)
		    {
		    	short nValue = (short)(objByteBuffer.get() & 0xFF);
				System.out.printf("\n%d> [%04X] = %d", i, nValue, nValue);
		    }
		}
	}
}
위 소스에서 첫 번째 부분의 unsigned byte 0xFA( = 250 )를 byte 형에 입력하였을 때, 250이 입력되는 것이 아니라, -6으로 입력됩니다. 이는 자바의 byte형이 unsigned를 지원하지 않기 때문입니다. 따라서 short에 그 값을 넣어도 원래 원했던 250이 입력되는 것이 아니라, -6( = 0xFFFA )가 입력되는 것입니다. 만약 그 값(-6)을 가지고 다른 계산식에 사용하게 되면, 엄청난 오차가 발생하게 됩니다.

그래서 이 문제를 해결하기 위해서는 위 소스와 아랫 부분과 같이 마스킹(Masking)을 사용하여 데이터가 올바로 변환되도록 해야 합니다. unsigned byte를 위해서는 1byte 만 마스킹(0xFF)하면 되고, unsigned short는 2byte(0xFFFF), unsigned int는 4byte를 마스킹(0xFFFFFFFF)하면 됩니다. 문제는 마스킹한 데이터를 받는 변수는 원 데이터형보다 한단계 높은 데이터형을 써야 그 값이 올바로 저장된다는 점입니다. unsigned byte는 short에 입력하고, unsigned short는 int에, unsigned int는 long에 입력해 줘야 합니다. unsigned long는 별도로 다르게 처리해 줘야 합니다. 이 부분은 나중에 기회가 되면 다뤄보겠습니다.

다음은 위 소스를 실행한 결과입니다.
===== unsigned byte to short (변환 오류 예) ===
unsigned byte(0xFF) to short : -6 ==> Hexa : 0xFFFA

===== ByteBuffer의 unsigned byte를 short로 변환(마스킹 사용) ===
[FF][FE][FD][FC]
0> [00FF] = 255
1> [00FE] = 254
2> [00FD] = 253
3> [00FC] = 252
위 결과를 보면, unsigned byte로 입력한 [FF][FE][FD][FC] 값들이 올바로 short로 변환되어 표시가 되는 것을 확인할 수 있습니다.

결론

네트워크를 통하여 데이터를 주고 받을 때, 상호간에 패킷 데이터의 구조를 일치시켜야만 올바로 데이터를 주고 받을 수 있습니다. 패킷의 구조상 short나 int형 데이터가 포함되어 있을 경우, byte order(Little Endian / Big Endian)가 매우 중요합니다. 동종 계열이라면 크게 상관없지만, 이기종의 장비간 데이터를 주고 받는 경우라면 동일하게 byte order를 맞추어야 합니다. 그리고 자바의 경우에는 부호없는(unsigned) 데이터형이 존재하지 않기 때문에 그에 대한 처리 또한 중요한 문제가 됩니다.

부호없는(unsigned) 데이터형인 경우에는 위 본문에서와 같이 데이터 형에 맞는 마스킹(Masking)을 통하여 바로 상위 데이터 형으로 변환하여야만 올바르게 계산이 가능합니다. 물론 그로 인하여 원치않는 메모리에 손실이 발생하는 것은 어쩔 수 없는 문제입니다.

참고자료