안드로이드

Python Server - Android Studio(Java) Client (TCP Socket)-(2)

렉스 2020. 11. 14. 20:38

https://hwanglex.tistory.com/3

 

Python Server - Android Studio(Java) Client (TCP Socket)-(1)

우리는 학교 서버를 대여해서 웹서버를 구축했다. 서버에는 파이썬 프로그램을 돌려놓고, 안드로이드 스튜디오와 소켓통신 알약 이미지가 안드로이드 -> 파이썬, 파이썬 -> 안드로이드로 왔다

hwanglex.tistory.com

첫번째 글에서 안드로이드->파이썬 이미지 전송, 파이썬->안드로이드 문자열 전송에 대해 이야기해보았다면

이번 글에서는 반대로 파이썬->안드로이드 이미지 전송, 안드로이드->파이썬 문자열 전송에 대해 이야기해보도록 하겠다.

 

<Android Studio: Client>

 

(1) 총 전송받을 이미지 개수 전송

  우리는 리스트뷰에 넣은 알약 이미지를 서버로부터 받아야했다. 따라서 jsonObject의 수, 즉 알약의 개수를 서버로 전송해야했다.    DataOutputStream의 메소드인 write()를 사용해서 전송한다.

void connect() {
        mHandler = new Handler();

        Log.w("connect", "연결 하는중");
        Thread checkUpdate = new Thread() {
            public void run() {
                // 서버 접속
                try {
                    socket = new Socket(ip, port);
                    Log.w("서버 접속됨", "서버 접속됨");
                } catch (IOException e1) {
                    Log.w("서버접속못함", "서버접속못함");
                    e1.printStackTrace();
                }

                Log.w(": ", "안드로이드에서 서버로 연결요청");

                try {
                    dis = new DataInputStream(socket.getInputStream());
                    dos = new DataOutputStream(socket.getOutputStream());

                } catch (IOException e) {
                    e.printStackTrace();
                    Log.w("버퍼", "버퍼생성 잘못됨");
                }
                Log.w("버퍼", "버퍼생성 잘됨");

                // 리스트뷰 참조 및 Adapter달기
                try {
                    JSONObject jsonObject = new JSONObject(mJsonString);
                    JSONArray jsonArray = jsonObject.getJSONArray(TAG_JSON);

                    pill_num = jsonArray.length();
                    img_list = new byte[pill_num][];
                    name_list = new String[pill_num];

                    try {
                        dos.write(pill_num);
                        dos.flush();
                        System.out.println("the number of image: " + Integer.toString(pill_num));

                    } catch (IOException e) {
                        Log.d(TAG, "send pill number error");
                    }

                    for (int i = 0; i < pill_num; i++) {
                        JSONObject item = jsonArray.getJSONObject(i);
                        int data_len = 0;

                        String img = item.getString(TAG_IMG);
                        name_list[i] = item.getString(TAG_NAME);

                        try {
                            dos.writeUTF(img); #이미지 주소 전송
                            dos.flush();

                            data_len = dis.readInt(); #

                        } catch (IOException e) {
                            Log.d(TAG, "send image name error");
                        }

                       
                        img_list[i] = InputStreamToByteArray(data_len, dis);
                        System.out.println("One img done");
                    }
                } catch (JSONException e) {
                    Log.d(TAG, "showResult : ", e);
                }
                try{
                    socket.close();
                }catch(IOException e){

                }
            }
        };
        checkUpdate.start();
        try {
            checkUpdate.join();
        }catch (InterruptedException e){

        }
        System.out.println("Thread terminated");
    }

(2) 이미지 path 서버로 전송

  서버에 전체 알약이 저장되어있고, DB에는 해당 알약의 서버 상 path만 저장해놓았다. 전송 받기 원하는 이미지의 path를 서버로 전송한다. 파이썬에서 안드로이드로 문자열 전송하는건 어려웠던 것 같은데 이 경우는 너무 간단했다.

 

(3) 서버로부터 이미지 byte 개수 수신

  역시 한번에 이미지를 다 받으려하면 버퍼가 가득 차서 안되더라.. 조금씩 나누어 받기 위해 먼저 이미지 전체 바이트 수를 전송받는다. 이번에는 평범하게 readInt() 메소드를 쓸 수 있었다.

 

(4) 서버로부터 이미지 수신

  정말 오랫동안 고생했던 파이썬->자바 이미지 전송..ㅠㅠ

  거의 3주 가까이 구글링을 해봤는데(물론 매일매일 하루종일 한건 아니다) 정말 안나와서 결국 내가 만들었다.. 물론 이게 맞는건지는 모르겠지만 제대로 동작하면 된거지

  파이썬에서 이미지를 받을 때와 마찬가지로 전체 이미지 바이트 수를 다 받을 때까지 readFully로 1024바이트 씩 수신한다. 바이트 배열 조각들은 resbytes 배열에 차곡차곡 붙인다. 마지막 loop에서 보내지는 바이트 수는 1024보다 작을 것이므로 loop를 빠져나온 뒤 딱 맞게 받아주었다.

    public byte[] InputStreamToByteArray(int data_len, DataInputStream in) { 
        int loop = (int)(data_len/1024);
        System.out.println("loop"+Integer.toString(loop));
        byte[] resbytes = new byte[data_len];
        int offset = 0;
        try {
            for (int i=0; i<loop; i++){
                in.readFully(resbytes, offset, 1024);
                offset += 1024;
            }
            in.readFully(resbytes, offset, data_len-(loop*1024));
        } catch (IOException e){
            e.printStackTrace();
        }
        return resbytes;
    }

 

 

<python: Server>

 

(1) 전송할 이미지 개수 안드로이드로부터 수신

  저번 글에서 이미지 크기를 문자열 타입으로 받았을 때와 마찬가지로 꼭 bytearray의 두번째 인덱스부터 디코딩해야한다. 

 

host = '우리 서버 IP 주소'
port = 8089

server_sock = socket.socket(socket.AF_INET)
server_sock.bind((host, port))
server_sock.listen(1)

idx = 0
while True:
    idx += 1
    print("기다리는 중")
    client_sock, addr = server_sock.accept()
    
    img_num = client_sock.recv(4);
    img_num = int.from_bytes(img_num, "little")
    
    print("이미지 개수:", img_num)
    
    for i in range(img_num):
        img_file_name = bytearray(client_sock.recv(1024))[2:]
        img_file_name = img_file_name.decode("utf-8")
        print("이미지 이름:", img_file_name)
        img_path = "img/"+img_file_name
        send_img(client_sock, img_path)
        print("Done")
               
    client_sock.close()

server_sock.close()

(2) 안드로이드 스튜디오로 이미지 전송하기

  send_img 함수를 생성했다. 먼저 이미지 byte 수를 전송한다. 이때도 역시 byteorder을 big으로 설정해야 제대로 전송된다.

  서버가 이미지 바이트 수를 받았다면 이제 while loop를 돌면서 이미지를 1024바이트 씩 전송한다.

def send_img(sock, img_path):
    img_f = open(img_path, "rb")
    data = img_f.read()
    data_len = len(data)
    sock.sendall(data_len.to_bytes(4, byteorder="big"))
    print("data length:", data_len)
    
    step = 1024
    loop = int(data_len/step)+1
    
    while len(data) > 0:
        if len(data) < step:
            sock.sendall(data)
            data = []
        else:
            sock.sendall(data[:step])
            data = data[step:]
    
    img_f.flush()
    img_f.close()

 

모델이 파이썬으로 작성되어서 파이썬으로 서버를 구축했더니,, 너무 자료가 없었다. 우리와 같이 안드로이드와 파이썬 소켓통신이 필요한 사람들에게 이 글이 도움이 되었으면 좋겠다.