0

I have taken reference of some tutoials on the web and written an Android App. The app is supposed to send out a command to the server, and then the server response a message (command and response is character-wise). After that my app will send out some files to the server. All of the them is communicated through TCP connection. I have tested it with a few PC TCP testing software, but it does not always sending the full file succesfully. Sometimes the TCP test tool hang and pop-up some error messages. I do not know if this is the problem of the PC tool or my app.

In fact I have another two prblems. One is that I do not know when should I close the socket. In the tutorial it close the socket in the "finally" routine, is it good? And actually when will it be closed? I want it to be closed after I send out all the files.

Another one is that I still not be able to receive response message send back from the server.

I am newbies of Android App development, so the code may look very silly, please guide me and tell me any problem, thank you very much.

Below is my code: I implement the TCP client as Async task

public class TCPClient {
    public static final String SERVERIP = "192.168.0.100"; //Server's IP address
    public static final int SERVERPORT = 4444; // Server's listening port

    private Socket socket;
    private OnMessageReceived mMessageListener = null;
    private boolean mRun = false;
    private static final String TAG = "TCPClient";

    PrintWriter out;
    BufferedReader in;

    /**
     *  Constructor of the class. OnMessagedReceived listens for the messages received from server
     */
    public TCPClient(OnMessageReceived listener) {
        mMessageListener = listener;
    }

    /**
     *  Sends message out
     * @param message text (ASCII string) to be send out
     */
    public void sendMessage(String message){
        Log.d(TAG, "Sent Message: " + message);
        if (out != null && !out.checkError()) {
            out.println(message);
            out.flush();
        }
    }

    /**
     * Send file out
     */
    public void sendFile(String filepath){

        try {
            BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

            if(bos != null){
                File myFile = new File( filepath );
                byte[] mybytearray = new byte[(int) myFile.length()];

                // Create InputStream object from the file
                FileInputStream fis = null;
                try {
                    fis = new  FileInputStream(myFile);
                } catch (FileNotFoundException ex) {
                    // Do exception handling
                }

                // Try to copy data from InputStream (file) to OutputStream (TCP socket)
                try {
                    int count;
                    BufferedInputStream bis = new BufferedInputStream(fis);
                    while ( (count = bis.read(mybytearray)) > -1 ){
                        bos.write(mybytearray, 0, count);
                    }
                } catch (IOException ex) {
                    // Do exception handling
                }

                bos.flush();
                bos.close();
            }
        } catch (IOException ex) {
            // Do exception handling
        }
    }


    public void stopClient(){
        Log.d(TAG, "Client stopped!");
        mRun = false;
    }

    public void run() {

        mRun = true;

        try {
            //here you must put your computer's IP address.
            InetAddress serverAddr = InetAddress.getByName(SERVERIP);

            Log.d(TAG, "C: Connecting...");

            //create a socket to make the connection with the server
            socket = new Socket(serverAddr, SERVERPORT);

            Log.d(TAG, "TCP connected");

            try {

                // A "Writer" for sending our command/message to the socket
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

                // A "Reader" for read back the response from the server
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                Log.d(TAG, "In/Out created");

                //in this while the client listens for the messages sent by the server
                while (mRun) {
                    serverMessage = in.readLine();

                    if (serverMessage != null && mMessageListener != null) {
                        //call the method messageReceived from MyActivity class
                        mMessageListener.messageReceived(serverMessage);
                    }
                    serverMessage = null;

                }

                Log.d(TAG, "S: Received Message: '" + serverMessage + "'");

            } catch (Exception e) {
                Log.e(TAG, "S: Error", e);
            } finally {
                //the socket must be closed. It is not possible to reconnect to this socket
                // after it is closed, which means a new socket instance has to be created.
                Log.d(TAG, "Close socket");
                socket.close();
            }
        } catch (Exception e) {
            Log.e("TCP", "C: Error", e);
        }
    }

    //Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity
    //class at on asynckTask doInBackground
    public interface OnMessageReceived {
        public void messageReceived(String message);
    }
}

This is the Async Task to connect to the server ( I implemented it in the MainActivity.java)

public class connectTask extends AsyncTask<String,String,TCPClient> {
        @Override
        protected TCPClient doInBackground(String... message) {

            Log.d(TAG, "In do in background");

            //we create a TCPClient object and
            mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
                @Override
                //here the messageReceived method is implemented
                public void messageReceived(String message) {

                    Log.d(TAG, "message received");

                    //this method calls the onProgressUpdate
                    publishProgress(message);
                }
            });
            mTcpClient.run();

            return null;
        }

        // When received message from server, do something here...
        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);

            Log.d(TAG, "In progress update, values: " + values.toString());
        }

    }

When I want to connect to the server:

new connectTask().execute("");

When I want to send out a file, I call the "send_my_file()" method in the MainActivity.java:

public static String test_filepath;
test_filepath = externalStoragePublicDirectory.getPath() + "/test.wav";

send_my_file(test_filepath);

public void send_my_file(String filepath)
{
    if (mTcpClient != null) {
        Log.d(TAG, "Start Send File");
        mTcpClient.sendFile(filepath);
    }
}

UPDATE:

1.) As it is marked as duplicated, I would like to clarify that I am asking about why only part of the file is sent to the TCP channel, but not asking why multiple files are not received correctly from the socket.

2.) Thank you for @EJP who gave me a link to a related queation. I refer to the answer there and reviewed the usage of read() of BufferedInputStream. I found that I misunderstood the usage of the read(myFile) method, which will return a single byte from the file. Originally I thought it will read the whole file and store it in a buffer. Instead, I should use read(buffer, 0, fileSize), which will read the file and store part of them to the buffer. Also for better efficiency, I should actually use a smaller buffer instead of creating a buffer with the same size with the file. I can send the whole file succesfully now. The fix is:

fileSize = myFile.length();
byte[] mybytearray = new byte[8192];
while ( fileSize > 0 && (count = bis.read(mybytearray, 0, (int)Math.min(mybytearray.length, fileSize)) ) > -1 ){
    bos.write(mybytearray, 0, count);
    fileSize -= count;
}
eepty
  • 716
  • 3
  • 10
  • 28
  • How is the server supposed to realize that it has received the full file? Also while you have an AsyncTask that gets *some* of your networking off the main thread, a quick read makes it seem like your attempt to send the file skips through that and tries to write to the network socket from the main thread? – Chris Stratton Jun 06 '16 at 03:33
  • @Chris Stratton The server will get the file length from the first command I send to the it. The flow is I send a command, which has the file name and file size, to the server. Then the server response a message, basically an ACK. Then I send out the file. Yes, haveing reviewed the code, I found that I am actually skipped the AsyncTask to send out the file. I am sorry about that, I am stil trying hard to understand them. – eepty Jun 06 '16 at 04:32
  • Your edit isn't correct. `read()` can only return zero if you supplied a zero length, which is a programming bug that you wouldn't want to smoke the CPU with. You do not need a buffer the size of the file. See my answer in the duplicate for how to do this properly. – user207421 Jun 06 '16 at 05:32
  • @EJP I do not understand why this question is duplicated with the question you mentioned. The question you mentioned asked why all files are written to the same file, and then got an answer of how to read a data stream from the socket and write it to the files. My question is asking why only part of one file is sending out to the TCP. (I am not writing the server part!). I think it could be a related question, but not duplicated. – eepty Jun 07 '16 at 17:07

0 Answers0