본문 바로가기
개발/android

Bitmap 이미지를 byte로 바로 보낸다고? 그것도 소켓으로?

by 매몰 2015. 11. 30.

 

 

군대를 막 전역했을때 다시 완벽한 민간인이 되기 위해 책한권을 샀었다

바로 "TCP/IP 소켓 프로그래밍" 이었다

 

이때 처음 접한 네크워크 코딩술로

간단한 원도우용 채팅프로그램을 만들었고 

졸업후에는 PC와 스마트폰간의 연결앱인 "키보드에디터"를 출시하였다. 물론 쫄닥 망했다ㅎㅎ

 

어쨌든 그후에도 가끔씩 소켓기반의 앱을 만들면서 자연스럽게 이미지 전송도 하게되었다

여기서는 비트맵이미지를 파일형태가 아닌 Byte단위의 원본으로 바로 보내는 것을 다뤄보겠다.

 

파일도 결국은 Byte로 보내는것이지만... 

외부파일이 아닌 내부이미지를 불러와 바로 전송할때 유용할것이다~

 

 

 

 

-서버

public class Server implements Runnable {

    @Override
    public void run() {
        try {
            ServerSocket socket = new ServerSocket(9999);

            while(true) {
                Socket clientsoket = socket.accept();
                
                //클라이언트로 보낼 출력스트림을 얻는다
                DataOutputStream os = new DataOutputStream(clientsoket.getOutputStream());

                //이미지를 불려온다
                byte[] data = getImageByte(BitmapFactory.decodeResource(getResources(), R.drawable.image));

                //비트맵이미지의 총크기를 4byte배열에 담아서 먼저 보낸다 (이때 꼭 4byte일 필요는 없다. 마음내키는대로~)
                //총크기를 보내는 이유는 비트맵이미지가 전부 전송된 시점을 클라이언트에 알리기 위함이다
                byte[] size = getByte(data.length);
                os.write(size, 0, size.length);
                os.flush();

                //실제 데이터를 보낸다
                os.write(data, 0, data.length);
                os.flush();
            }

            socket.close();

        } catch (IOException e) {
            e.printStackTrace();

        }
    }
}

//비트맵의 byte배열을 얻는다
public byte[] getImageByte(Bitmap bitmap) {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);

    return out.toByteArray();
}
//숫자를 byte형태로 바꾼다
private byte[] getByte(int num) {
    byte[] buf = new byte[4];
    buf[0] = (byte)( (num >>> 24) & 0xFF );
    buf[1] = (byte)( (num >>> 16) & 0xFF );
    buf[2] = (byte)( (num >>>  8) & 0xFF );
    buf[3] = (byte)( (num >>>  0) & 0xFF );

    return buf;
}

 

 

 

 

-클라이언트

public class Client implements Runnable {

    @Override
    public void run() {
        try {
            Socket socket = new Socket("IP주소", 9999);

            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

            while(mRun) {
                byte[] imagebuffer = null;
                int size = 0;

                byte[] buffer = new byte[10240];

                int read;
                while((read = bis.read(buffer)) != -1 && mRun) {
                    if (imagebuffer == null) {
                        //처음 4byte에서 비트맵이미지의 총크기를 추출해 따로 저장한다
                        byte[] sizebuffer = new byte[4];
                        System.arraycopy(buffer, 0, sizebuffer, 0, sizebuffer.length);
                        size = getInt(sizebuffer);
                        read -= sizebuffer.length;

                        //나머지는 이미지버퍼 배열에 저장한다
                        imagebuffer = new byte[read];
                        System.arraycopy(buffer, sizebuffer.length, imagebuffer, 0, read);
                    }
                    else {
                        //이미지버퍼 배열에 계속 이어서 저장한다
                        byte[] preimagebuffer = imagebuffer.clone();
                        imagebuffer = new byte[read + preimagebuffer.length];
                        System.arraycopy(preimagebuffer, 0, imagebuffer, 0, preimagebuffer.length);

                        System.arraycopy(buffer, 0, imagebuffer, imagebuffer.length - read, read);
                    }

                    //이미지버퍼 배열에 총크기만큼 다 받아졌다면 이미지를 저장하고 끝낸다 
                    if(imagebuffer.length >= size) {
                        Bundle bundle = new Bundle();
                        bundle.putByteArray("Data", imagebuffer);

                        Message msg = mResultHandler.obtainMessage();
                        msg.setData(bundle);
                        mResultHandler.sendMessage(msg);
                        
                        imagebuffer = null;
                        size = 0;
                    }
                    
                }
            }

            socket.close();
            bis.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

//byte배열을 숫자로 바꾼다
private int getInt(byte[] data) {
    int s1 = data[0] & 0xFF;
    int s2 = data[1] & 0xFF;
    int s3 = data[2] & 0xFF;
    int s4 = data[3] & 0xFF;

    return ((s1 << 24) + (s2 << 16) + (s3 << 8) + (s4 << 0));
}
//이미지뷰에 비트맵을 넣는다
public Handler mResultHandler = new Handler() {
    public void handleMessage(Message msg) {
        byte[] data = msg.getData().getByteArray("Data");

        ((ImageView) findViewById(R.id.ImageView)).setImageBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
    }
};

 

 

참고로~

언급했듯이 전송형태에 있어서

위 방법말고도 불러온 이미지를 파일로 만들어 보내는 형태도 있고,

 

이미지버퍼 배열을 데이터에 담는 방식에 있어서도

미리 받은 총크기로 배열을 생성한 뒤 계속 이어서 받는 방식도 있다

 

편한데로 응용해서 쓰시길 바란다

 

 

 

 

도움이 되셨다면~ 정성으로 빚은 저희 앱!  많은 이용 바래요:)

 

https://meorimal.com/index.html?tab=spaceship

 

우주선 - 방치형 인공지능 투자 체험기

미리 맛보는 인공지능 투자!

(주)머리말 meorimal.com

 

https://meorimal.com/subway.html

 

지하철어디있니

더이상 고민하지 마세요. 뛸지 말지 딱 보면 알죠.

(주)머리말 meorimal.com

 

사업자 정보 표시
주식회사 머리말 | 고영진 | 서울특별시 송파구 중대로 135 서관 10층 (가락동, 아이티벤처타워) | 사업자 등록번호 : 524-88-00727 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2017-서울강남-03941호 | 사이버몰의 이용약관 바로가기