우리는 학교 서버를 대여해서 웹서버를 구축했다.
서버에는 파이썬 프로그램을 돌려놓고, 안드로이드 스튜디오와 소켓통신
알약 이미지가 안드로이드 -> 파이썬, 파이썬 -> 안드로이드로 왔다 갔다 하기 때문에 모든 경우의 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)
...소켓통신은 티키타카가 너무 어렵다..
안드로이드에서 파이썬으로 이미지 전송, 파이썬에서 안드로이드로 문자열 전송을 다루었으니, 다음 글에서는 그 반대 경우에 대해 이야기하도록 하겠다.
TCP Socket 통신 관련 코드 (0) | 2022.02.26 |
---|---|
Python Server - Android Studio(Java) Client (TCP Socket) (5) | 2020.12.07 |
Python Server - Android Studio(Java) Client (TCP Socket)-(2) (0) | 2020.11.14 |
댓글 영역