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;
}