상세 컨텐츠

본문 제목

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

안드로이드

by 렉스 2020. 11. 14. 19:58

본문

우리는 학교 서버를 대여해서 웹서버를 구축했다.

서버에는 파이썬 프로그램을 돌려놓고, 안드로이드 스튜디오와 소켓통신

 

알약 이미지가 안드로이드 -> 파이썬, 파이썬 -> 안드로이드로 왔다 갔다 하기 때문에 모든 경우의 socket 통신을 구현해본 것 같다. 특히 파이썬 -> 안드로이드 이미지 전송은 구글링을 아무리 해도 안나와서... 결국 내가 어찌저찌 완성했다.

 

우리 프로그램 통신 구조는 다음과 같다.

<Android Studio: Client>

 

- 안드로이드 스튜디오에서 서버로 이미지 전송

(1) 이미지 byte 수 전송

  integer 값을 string으로 전환하여 writeUTF()로 전송했다.

  writeInt()를 사용하지 않은 이유는 정확히 기억나지는 않지만 파이썬에서 받는 과정에서 문제가 있었던 것 같다.

(2) byte 배열로 전환된 이미지 전송

  byteArray에는 안드로이드에서 촬영한 이미지가 담긴다. 이걸 byte 배열로 전환하여 서버로 전송한다.

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

        Log.w("connect","연결 하는중");
        Thread checkUpdate = new Thread() {
            public void run() {
                ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
                rotatedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArray);
                byte[] bytes = byteArray.toByteArray();

                // 서버 접속
                try {
                    socket = new Socket(ip, port);
                    Log.w("서버:", "서버 접속됨");
                } catch (IOException e1) {
                    Log.w("서버:", "서버접속못함");
                    e1.printStackTrace();
                }

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

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

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

                try{
                    dos.writeUTF(Integer.toString(bytes.length));
                    dos.flush();

                    dos.write(bytes);
                    dos.flush();

                    img_path = readUTF8(dis);
                    mark = readUTF8(dis);
                    socket.close();

                }
                catch (Exception e){
                    Log.w("error", "error occur");
                }
            }
        };
        checkUpdate.start();
        try {
            checkUpdate.join();
        }catch (InterruptedException e){

        }

- 서버에서 전송해준 이미지 저장 경로와 text detection 결과 받기

    public String readUTF8 (DataInputStream in) throws IOException {
        int length = in.readInt();
        byte[] encoded = new byte[length];
        in.readFully(encoded, 0, length);
        return new String(encoded, UTF8_CHARSET);
    }

  내가 구글링을 잘 못한 것일 수도 있지만 파이썬->안드로이드 문자열 전송은 참 고생스럽다..

  이미지 전송 때와 비슷하게 문자열 길이를 먼저 받고, 해당 크기의 byte 배열을 생성한 뒤 readFully로 정확한 길이의 문자열을 받았다.

 

<python: Server>

 

- 안드로이드 스튜디오로부터 전송된 이미지 서버에서 수신

(1) 이미지 byte 수 수신

  숫자지만 string type으로 전송된 byte 수를 recv한다. 분명 그리 길지 않겠지만 사람 일은 모르는 거니까 그냥 1024로 설정했다.

  너무 고생스러웠던 부분... integer를 string으로 전환해서 byte array로 전송했는데 첫 2바이트는 쓰레기 값이 들어있었다... 왜 계속 이상한 값이 안나오나하고 프린트해봤는데 2번째 index 값부터 decode했더니 원하는 값이 나왔다. string으로 decode한 뒤 원래 원했던 값인 integer로 변환한다.

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

server_sock = socket.socket(socket.AF_INET)
server_sock.bind((host, port))
server_sock.listen(1)
result = ''
idx = 0
while True:
    idx += 1
    print("기다리는 중")
    client_sock, addr = server_sock.accept()

    len_bytes_string = bytearray(client_sock.recv(1024))[2:]
    len_bytes = len_bytes_string.decode("utf-8")
    length = int(len_bytes)

    img_bytes = get_bytes_stream(client_sock, length)
    img_path = "pill_img_from_server/img"+str(idx)+str(addr[1])+".jpg"
    
    with open(img_path, "wb") as writer:
        writer.write(img_bytes)
    print(img_path+" is saved")
    
    img_name = 'img'+str(idx)+str(addr[1])+".jpg"
    ##이미지를 모델에 입력해서 result에 detection 결과 저장하는 코드##
    
    write_utf8(img_path, client_sock)
    write_utf8(result, client_sock)
    
    client_sock.close()

server_sock.close()

(2)  byte 배열로 전환된 이미지 전송

  전송 받은 문자열 길이를 알았으니 버퍼에 들어오는대로 조금조금씩 나누어서 바이트들을 받는다. 이걸 또 한번에 받으려니까 계속 잘려서 구글링을 했더니 조금씩 받더라.. 한번에 받는 방법이 분명 있겠지만 내가 구글링을 못한 것 같다.

def get_bytes_stream(sock, length):
    buf = b''
    try:
        step = length
        while True:
            data = sock.recv(step)
            buf += data
            if len(buf) == length:
                break
            elif len(buf) < length:
                step = length - len(buf)
    except Exception as e:
        print(e)
    return buf[:length]

- 이미지 저장 경로와 text detection 결과 전송

  둘 다 문자열이니까 utf-8로 인코딩 후 sendall() 함수로 전송한다.

  먼저 문자열의 개수를 보낸다. encoding한 문자열의 개수를 전송하는데 이때 byteorder을 big으로 설정해야 제대로 보내진다.

  그 다음 보내려던 문자열을 전송한다.

def write_utf8(s, sock):
    encoded = s.encode(encoding='utf-8')
    sock.sendall(len(encoded).to_bytes(4, byteorder="big"))
    sock.sendall(encoded)

...소켓통신은 티키타카가 너무 어렵다..

 

안드로이드에서 파이썬으로 이미지 전송, 파이썬에서 안드로이드로 문자열 전송을 다루었으니, 다음 글에서는 그 반대 경우에 대해 이야기하도록 하겠다.

관련글 더보기

댓글 영역