Android - Client closes connection an image is sent, before receiving a string back

Viewed 262

I have an android app that opens a socket connection with the server and sends an image to a python server. The server receives that image and is supposed to send a string back to confirm the image has been received. However the socket closes down after I end the Output stream, therefore the server receives the image but the client can't receive a string from the server because the client closed the connection. Therefore, what I want to do is return a string/text confirming the image has arrived to the user client before the socket closes.

This is my Python server that receives the image as bytes decodes and saves it to a directory then sends a message back:

from socket import *
import datetime
import cv2
import PIL.Image as Image
from PIL import ImageFile, Image
import io
import base64
import numpy as np
import pickle
import uuid


date_string = datetime.datetime.now().strftime("%Y-%m-%d-%H:%M")

port = 9999
s = socket(AF_INET, SOCK_STREAM)
s.bind(('', port))
s.listen(1)

while True:
    conn, addr = s.accept()
    img_dir = '/home/Desktop/frames_saved/'
    img_format = '.png'
    try:
        print("Connected by the ",addr)
        #date_string = datetime.datetime.now().strftime("%Y-%m-%d-%H:%M")
        filename = str(uuid.uuid4())
        with open(img_dir+filename+img_format, 'wb') as file:
            while True:
                data = conn.recv(1024*8)
                if data:
                    print(data)
                    try:
                        file.write(data)
                    except:
                        s = socket(AF_INET, SOCK_STREAM)
                        s.bind(('', port))
                        s.listen(1)
                        conn.sendall(("Hello World"))
                        

                    
                else:
                    print("no data")
    
                    break
    finally:
        conn.close() 

What I am trying to do is receive the encoded string in my android client and print/show a toast.

Android client code:

 public class SendImageClient extends AsyncTask<byte[], Void, Void> {


        @Override
        protected Void doInBackground(byte[]... voids) {
            isSocketOpen = true;
            try {
                Socket socket = new Socket("192.168.0.14",9999);
                OutputStream out=socket.getOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(out);
                BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                StringBuilder response = new StringBuilder();
                String line;

                while (isSocketOpen){
                    Log.d("IMAGETRACK", "Wrote to the socket[1]");
                    dataOutputStream.write(voids[0],0,voids[0].length);
                    while ((line = input.readLine()) != null)
                        Log.d("IMAGETRACK3", "Wrote to the socket[3]");
                    response.append(line);
                    Message clientmessage = Message.obtain();
                    clientmessage.obj = response.toString();
                    Log.d("[MESSAGE]", String.valueOf(clientmessage));
                    // Tries to receive a message from the server
                    out.close();
                    input.close();
                    if(isSocketOpen == false){
                        Log.d("CLOSED", "CLOSED CONNECTION");
                        socket.close();

                        break;
                    }
                }
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }


            return null;
        }
    }

Additionally I noticed that doing out.close(); will close the stream socket aswell. Here's the log after an image is sent and received by the server:

D/IMAGETRACK: Wrote to the socket[1]
D/IMAGETRACK: Wrote to the socket[1]
W/System.err: java.net.SocketException: Socket closed
W/System.err:     at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:124)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:161)
        at java.io.DataOutputStream.write(DataOutputStream.java:107)
        at MyApp.MainActivity$SendImageClient.doInBackground(MainActivity.java:2792)
        at MyApp.MainActivity$SendImageClient.doInBackground(MainActivity.java:2780)
        at android.os.AsyncTask$3.call(AsyncTask.java:394)
W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

If I try moving out.close(); after the while loop, the client will send an infinite amount of data to the server and when i close the server to end the infinite data being receive, instead of a 70Kb image, i will have a 10MB image or more depending on how log I received the bytes.

From the looks of it, I think I would need to somehow stop the image being sent without closing the server to listen to the image being sent back. How can I do that without closing the socket?

1 Answers

The reason that socket is closed is simple. I have sample program like this before. You have 2 options to do it. Your architecture for sending and receiving a file is not complete and correct.

One option is that you can define a protocol of sending and receiving by your own, which is not standard. For example, you can define special characters such as ##endoffile##, and send it after your image, then do not close the socket. Server understands that image is received and there is nothing more. Then in client side call receive method until the end of the socket, but before, you have to send data string back to confirm the image has been received, and client closes the socket when receives this string. Remember to add socket timeout wisely for the prevention of infinite socket waiting.

The second solution is that you can use standard protocols such as HTTP or FTP, but have to read standards of these protocols such as header and body values, and also send and receive files as multipart.

Related