byte[] 바이트 배열을 socket을 통해 쉽게 전송하는 방법

 C/C++을 통해 파일을 socket 통신으로 전송하는 경우 데이터를 char 배열을 통해 buffer의 크기를 고려하면서 전송을 해야 하기 때문에 프로그램을 설계할 때 상당히 많은 부분을 고려해야 되어 골치가 아프지요. 그러한 면에서 보았을 때 Java에서 제공하는 Socket 통신 기능들이 상당히 편해서 프로그래머들에게도 상당히 큰 부담을 줄여주는 점이 맘에 들곤 합니다. 이번 포스팅에서는 C/C++에서는 다루는 것이 다소 번거로운 byte 배열을 손쉽게 전송하는 방법에 대해 알아보도록 하겠습니다.


 안드로이드 프로그래밍을 하다 보면 이미지나 파일을 Socket을 통해 전송해야 되는 경우들이 많습니다. 만약 수신측이 C/C++로 짜여져 있으면 정해진 buffer로 나누어서 전송을 해야 하기 때문에 파일을 byte[] 배열 형식으로 변환한 후 socket에 실어서 전송해야 합니다. 프로그램을 설계할 때 도중에 자료가 누락되는 경우 원본의 손실 또한 발생하기 때문에 신중하게 프로그래밍을 해야 합니다.

 안드로이드 카메라의 경우 takePicture() 함수에 callback 함수를 통해 카메라에 찍힌 이미지를 아래와 같은 방식으로 byte[] 배열로 제공합니다. 프로그래머는 이를 통해 파일로 저장하거나 화면에 띄우는 등의 작업을 수행하게 됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
        private Camera.PictureCallback picb_remote = new Camera.PictureCallback() {
 
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                // TODO Auto-generated method stub
 
                ....
 
                camera.startPreview();
            }
        };
cs

 위의 이미지 형식의 데이터인 byte[] 배열을 Java 기반의 서버와 socket을 통해 어떤 방식으로 통신을 하면 가장 간편할까요? Java에서는 직렬화 된 Object 혹은 byte[] 배열을 손쉽게 전송할 수 있는 ObjectOutputStreamObjectInputStream을 제공합니다. 이를 사용하는 방식에 대해 좀 더 자세히 알아보겠습니다. 소스코드에서 각 중요한 내용을 주석을 통해 설명하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
        private Camera.PictureCallback picb_remote = new Camera.PictureCallback() {
 
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                // TODO Auto-generated method stub
                try {
                    //IP주소와 포트번호를 입력하여 Socket 통신을 시작합니다.
                    Socket sock = new Socket("127.0.0.1"8200);
                    //Socket으로부터 outputStream을 얻습니다.
                    OutputStream os = sock.getOutputStream();
                    //등록한 OutputStream을 ObjectOutputStream 방식으로 사용합니다.
                    ObjectOutputStream oos = new ObjectOutputStream(os);
 
                    //byte[] 파일을 object 방식으로 통째로 전송합니다.
                    oos.writeObject(data);
                
                    oos.close();
                    os.close();
                    sock.close();
                        
                } catch (UnknownHostException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
 
                camera.startPreview();
            }
        };
cs

 위의 소스코드에서 보이는 바와 같이 생성된 byte[] 배열을 그대로 writeObject() 매서드의 인자값에 등록을 하면 Java에서는 이를 그대로 Server 쪽으로 전송해줍니다. 아래는 byte[]를  수신하는 Server 측의 소스코드입니다. 중요한 부분은 주석으로 설명합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
            int port = 8200;
            //Server측에서 사용할 포트번호를 설정한 후 Socket 서버를 개방합니다.
            ServerSocket sock = new ServerSocket(port);
            //Client로부터 소켓 신호를 기다립니다.
            Socket socket = sock.accept();
            //Socket로부터 InputStream을 등록합니다.
            InputStream is = socket.getInputStream();
            //등록한 InputStream을 ObjectInputStream방식으로 사용합니다.
            final ObjectInputStream ois = new ObjectInputStream(is);
            
            //전송된 byte[] 데이터를 수신합니다.
            byte[] data = (byte[])ois.readObject();
            
            System.out.println("data size : " + data.length);
            
            ois.close();
            is.close();
            socket.close();
            sock.close();
cs


 위와 같은 방식으로 Socket 프로그램을 설계하시면 byte[] 배열 방식으로 되어있는 데이터 값을 Java 상에서 손쉽게 전송할 수 있습니다.