Page 1 of 1

Android Socket Tutorial - Part 1

#1 EndLessMind  Icon User is offline

  • Android Expert
  • member icon

Reputation: 271
  • View blog
  • Posts: 1,250
  • Joined: 13-March 09

Posted 13 August 2016 - 02:29 PM

Welcome

So, it's time for an other tutorial.
This time it's going to be about Android Socket. Mainly the client side of it.

This is going to be a 2 part tutorial as it would have be to long otherwise.

In this first part, I'll be covering:
  • Settings up a connection
  • Send data
  • Receive data




In part 2, I'll be covering:

  • Data structure examples for sending over network.
  • Listener to notify the clients parent(Activity or Service) about events(incoming data or errors)
  • Talk about how we are reducing the load of the CPU by interrupting the threads
  • Creating file server browser using code from Part 1



So, let's get started!


Introduction

So, what is Socket and TCP?

Socket is a handle the a program can pass to the API to gain access to a network connection.
Socket often use some form of protocol for the communication.

TCP is a widely used network protocol, and the one we'll use for this application.


By default, Socket in Android is used TCP.
For UDP, we would need to use DatagramSocket or MulticastSocket.

In this tutorial series, we'll stick to TCP and the client part of it.

For the server, I used c# and the .Net Framework.
I'll provide source for this, including compiled executable, but I'll not cover it
as that is not a part of the platform that this section of this forum is about.

I'll make a Java/Android server tutorial if interest exists.

A complete source for this will be available in Part 2.


TCP and why

TCP stand for Transmission Control Protocol.
This protocol provides a end-to-end data communication that
specifies how the data should be packetized, addressed, transmitted, routed and received.

TCP also provides flow-control, connection establishment and reliable transmitted of data.
As of a result, we know if the data was received at the other end and in what order, unlike UDP which does not allow neither.

As a downside, TCP ability's leves it with a bigger overhead than UDP.

But the pros outweigh the cons as we gain much reliability.
This is why TCP is not suitable for application like VoIP.
In that case, UDP is more suitable as we can afford if some transmissions are lost along the way.


Setting up our application project[
I assume that you are already familiar with creating a Android project, there for I'll only provide you with the settings I used.
Application name, project name and package name can be what ever you want.
Minimum Required SDK: API 8 (Android 2.2)
Target SDK: API 19 (Android 4.4)
Compile with: API 23 (Android 6.0)

And finally, select:
Blank Activity


Settings up our Socket
Before we start with the code, I need to inform you of one important thing.
All socket related operation (Connecting, sending and receiving) will be performed in threads.
Therefore these 3 operation will have their on runnables classes.

For simplicity, we'll be using nested classes for this, also each nested class is quite short.
If you don't know what nested class are, they are a class within an other class.

First of, create a class-file and name it TCPClient.
Then we need to create some instance variables.
We need:
  • A string named TAG for printing to the logcat.
  • A socket named connectionSocket.
  • A string named serverIp to hold the ip-address to the server.
  • A integer named serverPort to hold the port to connect on.



We'll also define a long named startTime used to get the time an operation took.

This is the instance variables we'll use for now.
	private static String TAG = "TCPClient"; //For debugging, always a good idea to have defined
	private String severIp =   "192.168.0.2";
	private long startTime = 0l;
	private int serverPort = 1234;
	private Socket connectionSocket;



Now, create a nested class called ConnectRunnable that implements Runnable.
This is where we'll create the socket and open the connection.

In the ConnectRunnable run-void, We will:

Create the internet protocol address:
InetAddress serverAddr = InetAddress.getByName(severIp);

Creating a new instance of socket:
connectionSocket = new Socket();

Open a connection to the serverIp and serverPort with a timeout of 5000ms:
connectionSocket.connect(new InetSocketAddress(serverAddr, serverPort), 5000);
The connect-method will block until a connection is established or throw an exception if timed out or error occurred.

The ConnectRunnable run-void will also contain some printouts to the logcat and the use of startTime variable.

This is what the ConnectRunnable will look like for now.
	public class ConnectRunnable implements Runnable {

		public void run() {
			try {
				
				Log.d(TAG, "C: Connecting...");
				
				InetAddress serverAddr = InetAddress.getByName(severIp);
				startTime = System.currentTimeMillis();
				
				//Create a new instance of Socket
				connectionSocket = new Socket();
				
				//Start connecting to the server with 5000ms timeout
				//This will block the thread until a connection is established
				connectionSocket.connect(new InetSocketAddress(serverAddr, serverPort), 5000);
				
				long time = System.currentTimeMillis() - startTime;
				Log.d(TAG, "Connected! Current duration: " + time + "ms");
			} catch (Exception e) {
				//Catch the exception that socket.connect might throw
			}
			Log.d(TAG, "Connetion thread stopped");
		}
	}


We assign the system current time in milliseconds to the startTime before we start an operation.
After the operation is is complete, we once again get the system current time in milliseconds and subtract the value of startTime.
The result is the amount of milliseconds that has passed since we assigned the value to startTime.

In this runnable, it will give us the time it took to connect to the server.

Now, to run this code, we'll create a method named Connect in out TCPClient class.
This method will accept 2 arguments: the server IP as a string and the server port as an integer.

Inside this method will we set the values for our instance variables: serverIp and serverPort, and then
run the ConnectRunnable in a thread.

That method will look like this
	public void Connect(String ip, int port) {
		severIp = ip;
		serverPort = port;
		new Thread(new ConnectRunnable()).start();
	}



We just create a thread and run it directly, as we don't need to do anything to it when it was completed it's work.

Congratulation, you are now able to connect to a server using the TCP protocol.


To teach a dog to sit, you'll need commands
All interaction between the client and the server will use commands.
They can be used to tell the server what we want, or to tell the client that it's going to receive.
As We're going to transmit both commands (string) and file data (bytes), we also
like to what how we should handle the data before we start reading it from the stream.

For this, We'll create a new class called TCPCommands.
This class will hold our commands as string and data types as integer.
Remember, these values are chosen at at random and does not mean anything for any application
besides our server and client. So they can be changed if you want to, just remember that they need to be
the same in both the server and the clients code.

As this is a simple example, we only need 3 commands and 2 data types.
They should be quite self explanatory.
The data types are:
	public static int TYPE_CMD = 1; //For commands. They will be strings
	public static int TYPE_FILE_CONTENT = 2;//For files. They will be byte arrays



The commands are:
    public static String CMD_REQUEST_FILES = "server_get_files"; //When client request a list of files in that folder
    public static String CMD_REQUEST_FILES_RESPONSE = "server_get_files_response"; //When server respons with a list of files
    public static String CMD_REQUEST_FILE_DOWNLOAD = "server_download_file"; //When client request a file to be transfererad from the server.
	



The finished TCPCommands class should look like this
	public class TCPCommands {
	
	public static int TYPE_CMD = 1;
	public static int TYPE_FILE_CONTENT = 2;

    public static String CMD_REQUEST_FILES = "server_get_files";
    public static String CMD_REQUEST_FILES_RESPONSE = "server_get_files_response";
    public static String CMD_REQUEST_FILE_DOWNLOAD = "server_download_file";
}



Sending data
Well, all connections in the world are not going to help you if you're not using it for anything.
That's what we're going to do next.

The data will be sent in 3 steps:
  • An integer with the length of the data to be sent
  • An integer describing the type of data to be sent (either TYPE_CMD or TYPE_FILE_CONTENT will be used)
  • The actual data as a byte array (commands will be converter from string to byte-array)



Let's start of by creating a nested class inside TCPClient named SendRunnable, this should also implement Runnable.
This class will be a bit different from the ConnectRunnable.

This class is going to have a constructor and 2 methods other than the run-method.
The constructor should accept 1 argument, a Socket.
The 2 methods we're going to use is public void Send(byte[] bytes) and public void SendCMD(byte[] bytes)

We're also going to need 4 instance variables as follows:
  • A byte array that holds data to send.
  • A OutputStream that will be the output stream of the socket.
  • A boolean to indicate if there is any data pending to be sent.
  • A int to hold the data type to be sent.


Are you a bit confused? I would almost be worried if you weren't.
Don't worry, it will all make sense in a second.

This is the instance variables we'll need for the SendRunnable

		byte[] data;
		private OutputStream out;
		private boolean hasMessage = false;
		int dataType = 1;


First of, the constructor.
This will do 2 things:
  • Take the Socket argument and set it to the socket instance variable.
  • Get the output stream from the socket and set it to out OutputStream instance variable


that would look like this:
		public SendRunnable(Socket server) {
			try {
				//We need to catch any potential IOExceptions that getOutputStream might throw
				this.out = server.getOutputStream();
			} catch (IOException e) {
			}
		}



Now I think it's time to take a look at the methods that I mentioned earlier.

The Send method should do 3 things:
  • Take the byte[] argument and set it to the data instance variable
  • Set the dataType instance variable to TYPE_FILE_CONTENT
  • Set hasMessage to true


That should look like this
		public void Send(byte[] bytes) {
			this.data = bytes;
			dataType = TCPCommands.TYPE_FILE_CONTENT;
			this.hasMessage = true;
		}



The SendCMD method should to the same with 1 different:
1. Set dataType instance variable to TYPE_CMD
  • Set dataType instance variable to TYPE_CMD


The result should be:
		public void SendCMD(byte[] bytes) {
			this.data = bytes;
			dataType = TCPCommands.TYPE_CMD;
			this.hasMessage = true;
		}



Now, to the most important method, the run method.
So, this method should do a number of things:
  • Start while-loop running until the thread is interrupted or socket has disconnected
  • if hasMessage is true
    • Write the length of the byte array to the OutputStream.
    • Write the dataType if the data to the OutputStream.
    • Write the actual byte array of the data to the OutputStream.
    • Flush the stream to make sure everything has ben written out.

  • Set hasMessage to false
  • Set data to null (maybe not necessary, but an old c++ habit with pointers :P/> )



That's quite a bit, and later we'll add 2 more line of code when we're done with the ReceiveRunnable.

The only thing that's going to be a bit difficult to understand is when we convert integer to byte-array with this line
ByteBuffer.allocate(4).putInt(someInteger).array()
This will convert the integer to a big-endian byte-array.

So, this is what the run-method will look like:
		@Override
		public void run() {
			Log.d(TAG, "Sending started");
			while (!Thread.currentThread().isInterrupted() && isConnected()) {
				if (this.hasMessage) {
					startTime = System.currentTimeMillis();
					try {
						//Send the length of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(data.length).array());
						//Send the type of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(dataType).array());
						//Send the data
						this.out.write(data, 0, data.length);
						//Flush the stream to be sure all bytes has been written out
						this.out.flush();
					} catch (IOException e) { }
					this.hasMessage = false;
					this.data =  null;
					long time = System.currentTimeMillis() - startTime;
					Log.d(TAG, "Command has been sent! Current duration: " + time + "ms");
				}
			}
			Log.d(TAG, "Sending stopped");
		}


Again, I'm using the startTime variable to get the time it took to send the data.


So, the complete SendRunnable should look like this

	public class SendRunnable implements Runnable {

		byte[] data;
		private OutputStream out;
		private boolean hasMessage = false;
		int dataType = 1;
		
		public SendRunnable(Socket server) {
			try {
				this.out = server.getOutputStream();
			} catch (IOException e) {
			}
		}
		
		/**
		 * Send data as bytes to the server
		 * @param bytes
		 */
		public void Send(byte[] bytes) {
			this.data = bytes;
			dataType = TCPCommands.TYPE_FILE_CONTENT;
			this.hasMessage = true;
		}
		
		/**
		 * Send a command/message to the server
		 * @param bytes
		 */
		public void SendCMD(byte[] bytes) {
			this.data = bytes;
			dataType = TCPCommands.TYPE_CMD;
			this.hasMessage = true;
		}
		
		@Override
		public void run() {
			Log.d(TAG, "Sending started");
			while (!Thread.currentThread().isInterrupted() && isConnected()) {
				if (this.hasMessage) {
					startTime = System.currentTimeMillis();
					try {
						//Send the length of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(data.length).array());
						//Send the type of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(dataType).array());
						//Send the data
						this.out.write(data, 0, data.length);
						//Flush the stream to be sure all bytes has been written out
						this.out.flush();
					} catch (IOException e) { }
					this.hasMessage = false;
					this.data =  null;
					long time = System.currentTimeMillis() - startTime;
					Log.d(TAG, "Command has been sent! Current duration: " + time + "ms");
				}
			}
			Log.d(TAG, "Sending stopped");
		}
	}



That wasn't to complicated, right? :)/>
You should be getting an error on isConnected() was we haven't created this method yet, but we will.


So, how do we use this now?
First of, we need to add 3 more instance variables to the TCPClient class.
  • A variable for the SendRunnable class
  • A Thread variable to be used to run the SendRunnable
  • A byte-array variable to hold the data we want to send.


They should look like this:
	private SendRunnable sendRunnable;
	private Thread sendThread;
	byte[] dataToSend;


Then we're going to add 4 methods to the TCPClient class:
public void WriteData(byte[] data)
public void WriteCommand(String cmd)
public boolean isConnected()
and
private void startSending()

The method startSending should:
  • Create a new instance of SendRunnable
  • Create a new thread with the SendRunnable instance as an argument
  • Start the thread


Smal and simple:
	private void startSending() {
		sendRunnable = new SendRunnable(connectionSocket);
		sendThread = new Thread(sendRunnable);
		sendThread.start();
	}


Moving on to the isConnected method, it return true if:
  • connectionSocket is not null
  • connectionSocket is connected
  • connectionSocket is not closed

	public boolean isConnected() {
		return connectionSocket != null && connectionSocket.isConnected() && !connectionSocket.isClosed();
	}



We need to use connectionSocket.isClosed() because isConnected will always return true after it has been connected, even if
the connection has been closed. So we need to check both of them.

Now, WriteData and WriteCommand is very similar. Both:
  • Check if the socket is connected
  • Calls startSending


The different is in what method of SendRunnable they are calling.
It will be obvious when you look at the code below.

	public void WriteData(byte[] data) {
		if (isConnected()) {
			startSending();
			sendRunnable.Send(data);
		} 
	}
	
	public void WriteCommand(String cmd) {
		if (isConnected()) {
			startSending();
			sendRunnable.SendCMD(cmd.getBytes());
		}
	}


So, WriteData calls Send and WriteCommand calls SendCMD.
If you're wondering why I'm converting the command to byte-array, then it's because it simpler to send
everything in the same format. In this case, I send everything as raw byte-array.


Let's move on to the last section of Part 1 of this tutorial.


Receiving data
Now you might think that the SendRunnable where quite long and complicated.
Well, it might look like it, but it's just me trying to be thorough when explaining how it all works.
The next part is a bit more complicated, but I'll try to explain it well.

Now, create a new nested class inside TCPClient and name it ReceiveRunnable.
This class should also implement Runnable.

This class is going to have 2 instance variable
  • A InputStream that will be the input stream of the socket.
  • A Socket that will work as a reference to the socket that is connected to the server.



	private Socket sock;
	private InputStream input;



We're also going to add 3 more instance variable for the TCPClient class:
  • A variable for the ReceiveRunnable class
  • A Thread variable to be used to run the ReceiveRunnable
  • A boolean to know if the receive thread is running.

	private ReceiveRunnable receiveRunnable;
	private Thread receiveThread;
	boolean receiveThreadRunning = false;


The constructor will be almost the same as for SendRunnable, but we change getOutputStream to getInputStream.
Like so:
	public ReceiveRunnable(Socket server) {
		sock = server;
		try {
			input = sock.getInputStream();
		} catch (Exception e) { }
	}


Now we only have the run-method left.
So, this method should also do a number of things:
  • Start while-loop running until the thread is interrupted or socket has disconnected
  • Change receiveThreadRunning to true if it's false
  • Allocate a byte-array with 4 bytes for integers
  • Read the length integer send by server as byte-array
    • Convert that byte-array to and integer

  • Read the data type integer send by server as byte-array
    • Convert that byte-array to and integer

  • Create some variables to keep track of the data received.
  • if-statement on the data type integer to determine how to read the data
    • Read it as a command (string)
    • Read it as file content

  • Interrupt both send and receive thread.
  • change receiveThreadRunning to false when we're done



Everything except Nr.9 will be done inside the while-loop.

Let's get started. I'm going to split the code into bigger chunks to make it easier to go through it.

First of, we need a while-loop just like in SendRunnable.
	while (!Thread.currentThread().isInterrupted() && isConnected()) {
		//We'll do our work here
	}




Then we want to change the state of receiveThreadRunning to true, but only if is isn't true already.
	if (!receiveThreadRunning)
		receiveThreadRunning = true;


Now, as we're going to use a IO-object we're going to wrap all the read-calls inside a try-catch.
	try {
		//We'll do all the operations here.
	} catch (Exception e) {} 


First, we're going to allocate a byte-array with 4 bytes for reading the length integer and the data type integer.

	byte[] data = new byte[4];
	//Read the first integer, it defines the length of the data to expect
	input.read(data,0, data.lenght);
	int length = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
	//Read the second integer, it defines the type of the data to expect
	input.read(data,0,data.length);
	int type = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();


Now, we're going to create 2 variables to keep track of of the data receive.
We're going to need a read integer and a downloaded integer.

	int read = 0;
	int downloaded = 0;



Next we need a if-statement to do the right operations depending on the data type integer.
	if (type == TCPCommands.TYPE_CMD) {
		//We're expecting a command/message from the server (Like a list of files)
	} else if (type == TCPCommands.TYPE_FILE_CONTENT) {
		//We're expecting a file/raw bytes from the server (Like a file)
	}




Operation for TYPE_CMD
	//Allocate byte array large enough to contain the data to come
	data = new byte[length];
	StringBuilder sb = new StringBuilder();
	InputStream bis = new BufferedInputStream(input);
						
	//Read until all data is read or until we have read the expected amount
	while ((read = bis.read(data)) != -1) {
		downloaded += read;
		sb.append(new String(data,0, read, "UTF-8")); //Append the data to the StringBuilder
		if (downloaded == length) //We have what we expected, break out of the loop
			break;
	}


Here we first allocate a byte-array large enough to hold the data we're expecting.
Note: We wont to this for a file, as what might cause a OutOfMemory exception.

Then we create a StringBuilder so that we can assemble the command back together.
We're also creating a BufferedInputStream from the InputStream.

Next, we're looping until we read no data at all.
By using the byte-array as an argument to read, we automatically tell it that
we want to read data equal to the length of the byte-array.
The read variable will return the amount we actually read.

Then we'll add that downloaded variable.
Next we convert the byte-array to a string and append it to the final string using the StringBuilder.
Where we use the read variable to set what amount of the byte-array is actually bytes that we read. For example:
If the byte-array is 2048 bytes long, but we only read 1024, we don't want to convert the other 1024 unused bytes to string
as they will not contain any usable data.


The next line might seem a bit weird as we break out of the loop when we have read the amount of bytes that we expected the data to be.
This is because if you have a higher downstream bitrate that the server has upstream bitrate, then you might end up reading the data
faster than the server is sending it. In that case, read(data) might return -1 as there is not more data available in the stream at that moment.
That does not necessarily mean that the transfer is complete.
Sometimes read(data) might return 0 even if we already read all the data that we expected, and that will result is us never breaking out of the loop.

We fix both these scenarios with those 2 lines of code.

Operation for TYPE_FILE_CONTENT
	byte[] inputData = new byte[2048];
	InputStream bis = new BufferedInputStream(input);
		
	//Read until all data is read or until we have read the expected amount
	while ((read = bis.read(inputData)) != -1) {
		//Buffer loop
		downloaded += read;
		if (downloaded == length)//We have what we expected, break out of the loop
			break;
	}


Again, we allocate a byte-array with the length of 2048. We could use anything from 1k to 32k without any issue.
But to large or to small buffer will throttle, 2k-4k is a good number.

Then we create a BufferedInputStream from the InputStream.
The reading loop is basically the same as the one in TYPE_CMD, but we're just not converting the bytes to anything.

You might notice that we're not doing anything with the data.
That will be taken care of by the context, that will create the TCPClient instance, later on in the next part when
we implement listeners.

We'll finish off this run method by settings receiveThreadRunning to false after we exist our while-loop.
 receiveThreadRunning = false; 




Now, before we end this part of the tutorial we want to add a 3 new methods to our TCPClient class.
First, just as we have startSending() we'll also have a startReceiving() method.
Secondly, We'll have stopThreads() that will stop both the sendThread and the receiveThread so we're not using up CPU-cycles when
Lastly, We'll add a Disconnect method.
we're not expecting any more data to be sent or received.

So, startReceiving() will be just as startSending(), but with receiveRunnable and receiveThread.
	private void startSending() {
		sendRunnable = new SendRunnable(connectionSocket);
		sendThread = new Thread(sendRunnable);
		sendThread.start();
	}



stopThreads() will just interrupt both sendThread and receiveThread if they are not null.
This will cause isInterrupted to be true and therefore break out of the while-loop in both threads.
	private void stopThreads() {
		if (receiveThread != null)
			receiveThread.interrupt();
		
		if (sendThread != null)
			sendThread.interrupt();
	}



Disconnect() will simply stop the threads and close the socket.
	public void Disconnect() {
		stopThreads();
		
		try {
			connectionSocket.close();
			Log.d(TAG,"Disconnected!");
		} catch (IOException e) { }
			
	}


Now we're going to add stopThreads inside the try-catch in receiveRunnable, right at the end of the try and just before catch.
Also, we'll add Disconnect() inside catch so it disconnects if anything goes wrong.

Our receiveRunnable should now look like this:
	public class ReceiveRunnable implements Runnable {
		private Socket sock;
		private InputStream input;

		public ReceiveRunnable(Socket server) {
			sock = server;
			try {
				input = sock.getInputStream();
			} catch (Exception e) { }
		}
		
		@Override
		public void run() {
			Log.d(TAG, "Receiving started");
			while (!Thread.currentThread().isInterrupted() && isConnected()) {
				if (!receiveThreadRunning)
					receiveThreadRunning = true;
				
				startTime = System.currentTimeMillis();
				try {			
					byte[] data = new byte[4];
					//Read the first integer, it defines the length of the data to expect
					input.read(data,0,data.length);
					int length = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
					
					//Read the second integer, it defines the type of the data to expect
					input.read(data,0,data.length);
					int type = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
					
					int read = 0; int downloaded = 0;
					
					if (type == TCPCommands.TYPE_CMD) {
						//We're expecting a command/message from the server (Like a list of files)
						
						//Allocate byte array large enough to contain the data to come
						data = new byte[length];
						StringBuilder sb = new StringBuilder();
						InputStream bis = new BufferedInputStream(input);
						
						//Read until all data is read or until we have read the expected amount
						while ((read = bis.read(data)) != -1) {
							downloaded += read;
							sb.append(new String(data,0, read, "UTF-8")); //Append the data to the StringBuilder
							if (downloaded == length) //We have what we expected, break out of the loop
								break;
						}
						if (mList != null)
							mList.OnMessageReceived(sb.toString().replace("\n", "")); //Listener notifying that we got some cmd/msg
					} else if (type == TCPCommands.TYPE_FILE_CONTENT) {
						//We're expecting a file/raw bytes from the server (Like a file)
						
						
						if (mList != null)
							mList.OnFileIncoming(length, downloaded); //Listener notifying incoming data
						//We download the data 2048 bytes at the time
						byte[] inputData = new byte[2048];
						InputStream bis = new BufferedInputStream(input);
						
						//Read until all data is read or until we have read the expected amount
						while ((read = bis.read(inputData)) != -1) {
							//Buffer loop
							downloaded += read;
							if (mList != null)
								mList.OnFileDataReceived(inputData, read, length, downloaded);//Listener notifying more data has been transferred
							if (downloaded == length)//We have what we expected, break out of the loop
								break;
						}
						
						if (mList != null) 
							mList.OnFileComplete(downloaded, length);//Listener notifying file transferring complete
					}
					
					long time = System.currentTimeMillis() - startTime;
					Log.d(TAG, "Data received! Took: " + time + "ms and got: " + (downloaded + 8) + "bytes");

					//Stop listening so we don't have e thread using up CPU-cycles when we're not expecting data
					stopThreads();
				} catch (Exception e) {
					Disconnect(); //Gets stuck in a loop if we don't call this on error!
				}
			}
			receiveThreadRunning = false;
			Log.d(TAG, "Receiving stopped");
		}
		
	}


Again, I'm using the startTime variable to get how long it took to receive the data.
I'm also printing the information to the logcat. You should know about this by now, so that's why I'm not covering it.
If you don't know about this, check out the tutorial on debugging.


Ending the code for this part will be 2 lines to the sendRunnable that I talked about earlier.
	if(!receiveThreadRunning)
		startReceiving();


This will be added at the end of the if-statement: if(this.hasMessage)


That will result in our run-method for sendRunnable to look like this:
		@Override
		public void run() {
			Log.d(TAG, "Sending started");
			while (!Thread.currentThread().isInterrupted() && isConnected()) {
				if (this.hasMessage) {
					startTime = System.currentTimeMillis();
					try {
						//Send the length of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(data.length).array());
						//Send the type of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(dataType).array());
						//Send the data
						this.out.write(data, 0, data.length);
						//Flush the stream to be sure all bytes has been written out
						this.out.flush();
					} catch (IOException e) { }
					this.hasMessage = false;
					this.data =  null;
					long time = System.currentTimeMillis() - startTime;
					Log.d(TAG, "Command has been sent! Current duration: " + time + "ms");
					if (!receiveThreadRunning)
						startReceiving(); //Start the receiving thread if it's not already running
				}
			}
			Log.d(TAG, "Sending stopped");
		}



Your TCPClient should now look something like this:
public class TCPClient {
	private static String TAG = "TCPClient"; //For debugging, always a good idea to have defined
	
	boolean receiveThreadRunning = false;
	private long startTime = 0l;
	
	private Socket connectionSocket;
	
	//Runnables for sending and receiving data
	private SendRunnable sendRunnable;
	private ReceiveRunnable receiveRunnable;
	//Threads to execute the Runnables above
	private Thread sendThread;
	private Thread receiveThread;
	
	byte[] dataToSend;
	private String severIp =   "192.168.0.2";
	private int serverPort = 1234;
	

	
	/**
	 * Returns true if TCPClient is connected, else false
	 * @return Boolean
	 */
	public boolean isConnected() {
		return connectionSocket != null && connectionSocket.isConnected() && !connectionSocket.isClosed();
	}
	
	/**
	 * Open connection to server
	 */
	public void Connect(String ip, int port) {
		severIp = ip;
		serverPort = port;
		dataToSend = null;
		new Thread(new ConnectRunnable()).start();
	}
	
	/**
	 * Close connection to server
	 */
	public void Disconnect() {
		stopThreads();
		
		try {
			connectionSocket.close();
			Log.d(TAG,"Disconnected!");
		} catch (IOException e) { }
			
	}
	
	/**
	 * Send data to server
	 * @param data byte array to send
	 */
	public void WriteData(byte[] data) {
		if (isConnected()) {
			startSending();
			sendRunnable.Send(data);
		} 
	}
	
	/**
	 * Send command to server 
	 * @param cmd Commands as string to send
	 */
	public void WriteCommand(String cmd) {
		if (isConnected()) {
			startSending();
			sendRunnable.SendCMD(cmd.getBytes());
		}
	}
	
	private void stopThreads() {
		if (receiveThread != null)
			receiveThread.interrupt();
		
		if (sendThread != null)
			sendThread.interrupt();
	}
	
	private void startSending() {
		sendRunnable = new SendRunnable(connectionSocket);
		sendThread = new Thread(sendRunnable);
		sendThread.start();
	}
	
	private void startReceiving() {
		receiveRunnable = new ReceiveRunnable(connectionSocket);
		receiveThread = new Thread(receiveRunnable);
		receiveThread.start();
	}
	
	public class ReceiveRunnable implements Runnable {
		private Socket sock;
		private InputStream input;

		public ReceiveRunnable(Socket server) {
			sock = server;
			try {
				input = sock.getInputStream();
			} catch (Exception e) { }
		}
		
		@Override
		public void run() {
			Log.d(TAG, "Receiving started");
			while (!Thread.currentThread().isInterrupted() && isConnected()) {
				if (!receiveThreadRunning)
					receiveThreadRunning = true;
				
				startTime = System.currentTimeMillis();
				try {			
					byte[] data = new byte[4];
					//Read the first integer, it defines the length of the data to expect
					input.read(data,0,data.length);
					int length = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
					
					//Read the second integer, it defines the type of the data to expect
					input.read(data,0,data.length);
					int type = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
					
					int read = 0; int downloaded = 0;
					
					if (type == TCPCommands.TYPE_CMD) {
						//We're expecting a command/message from the server (Like a list of files)
						
						//Allocate byte array large enough to contain the data to come
						data = new byte[length];
						StringBuilder sb = new StringBuilder();
						InputStream bis = new BufferedInputStream(input);
						
						//Read until all data is read or until we have read the expected amount
						while ((read = bis.read(data)) != -1) {
							downloaded += read;
							sb.append(new String(data,0, read, "UTF-8")); //Append the data to the StringBuilder
							if (downloaded == length) //We have what we expected, break out of the loop
								break;
						}
					} else if (type == TCPCommands.TYPE_FILE_CONTENT) {
						//We're expecting a file/raw bytes from the server (Like a file)
						//We download the data 2048 bytes at the time
						byte[] inputData = new byte[2048];
						InputStream bis = new BufferedInputStream(input);
						
						//Read until all data is read or until we have read the expected amount
						while ((read = bis.read(inputData)) != -1) {
							//Buffer loop
							downloaded += read;
							if (downloaded == length)//We have what we expected, break out of the loop
								break;
						}
						
					}
					
					long time = System.currentTimeMillis() - startTime;
					Log.d(TAG, "Data received! Took: " + time + "ms and got: " + (downloaded + 8) + "bytes");

					//Stop listening so we don't have e thread using up CPU-cycles when we're not expecting data
					stopThreads();
				} catch (Exception e) {
					Disconnect(); //Gets stuck in a loop if we don't call this on error!
				}
			}
			receiveThreadRunning = false;
			Log.d(TAG, "Receiving stopped");
		}
		
	}
	
	public class SendRunnable implements Runnable {

		byte[] data;
		private OutputStream out;
		private boolean hasMessage = false;
		int dataType = 1;
		
		public SendRunnable(Socket server) {
			try {
				this.out = server.getOutputStream();
			} catch (IOException e) {
			}
		}
		
		/**
		 * Send data as bytes to the server
		 * @param bytes
		 */
		public void Send(byte[] bytes) {
			this.data = bytes;
			dataType = TCPCommands.TYPE_FILE_CONTENT;
			this.hasMessage = true;
		}
		
		/**
		 * Send a command/message to the server
		 * @param bytes
		 */
		public void SendCMD(byte[] bytes) {
			this.data = bytes;
			dataType = TCPCommands.TYPE_CMD;
			this.hasMessage = true;
		}
		
		@Override
		public void run() {
			Log.d(TAG, "Sending started");
			while (!Thread.currentThread().isInterrupted() && isConnected()) {
				if (this.hasMessage) {
					startTime = System.currentTimeMillis();
					try {
						//Send the length of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(data.length).array());
						//Send the type of the data to be sent
						this.out.write(ByteBuffer.allocate(4).putInt(dataType).array());
						//Send the data
						this.out.write(data, 0, data.length);
						//Flush the stream to be sure all bytes has been written out
						this.out.flush();
					} catch (IOException e) { }
					this.hasMessage = false;
					this.data =  null;
					long time = System.currentTimeMillis() - startTime;
					Log.d(TAG, "Command has been sent! Current duration: " + time + "ms");
					if (!receiveThreadRunning)
						startReceiving(); //Start the receiving thread if it's not already running
				}
			}
			Log.d(TAG, "Sending stopped");
		}
	}
	
	public class ConnectRunnable implements Runnable {

		public void run() {
			try {
				
				Log.d(TAG, "C: Connecting...");
				InetAddress serverAddr = InetAddress.getByName(severIp);
				startTime = System.currentTimeMillis();
				//Create a new instance of Socket
				connectionSocket = new Socket();
				
				//Start connecting to the server with 5000ms timeout
				//This will block the thread until a connection is established
				connectionSocket.connect(new InetSocketAddress(serverAddr, serverPort), 5000);
				
				long time = System.currentTimeMillis() - startTime;
				Log.d(TAG, "Connected! Current duration: " + time + "ms");


			} catch (Exception e) {
			}
			Log.d(TAG, "Connetion thread stopped");
		}
	}
}



That's all for part 1!
I'll not provide any projectfiles here, they will be provided in Part 2 when the application actually does something :)/>

Is This A Good Question/Topic? 1
  • +

Replies To: Android Socket Tutorial - Part 1

#2 akshaypatil1234  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 29-November 16

Posted 29 November 2016 - 07:26 AM

Hi...
I am new to this developing world. So my question also will be like really basic!

I want to make an app something similar to lets say snapchat.
First I am looking for how users will register their account on my app. Like log in, sign up, forget password coding.
Do you have that coding related tutorial/forum?

I read some of thread posted by you and I must say it is well explained. So maybe if you have something related to sign up/log in and saving user data would be really helpful for me.
Was This Post Helpful? 0
  • +
  • -

#3 modi123_1  Icon User is online

  • Suitor #2
  • member icon



Reputation: 13491
  • View blog
  • Posts: 53,888
  • Joined: 12-June 08

Posted 29 November 2016 - 07:35 AM

@aksh - I would advocate taking a pencil and paper route and sketch out what you think is needed and how a registering system would work. Would a database be needed? What logic would you need to store, retrieve, compare, update, etc the data? Security? See where that leads you, and if you have questions to start a new thread in the appropriate help subforum.

Good luck!
Was This Post Helpful? 1
  • +
  • -

#4 akshaypatil1234  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 29-November 16

Posted 29 November 2016 - 07:44 AM

Hello,

@modi123_1

Actually I have some sketches made in advanced cause I was looking it online. So I have basic drawings like How I want my app UI to be look like, then what will be the next screen, and what function it will provide etc etc.

But I have really basic doubts like How do I connect those screens in the code then where user's log in data like email id, password, username will be stored and related things.

Which subforum you can suggest me to open a new thread? Sorry I am new to this website rules.

Thanks :)
Was This Post Helpful? 0
  • +
  • -

#5 modi123_1  Icon User is online

  • Suitor #2
  • member icon



Reputation: 13491
  • View blog
  • Posts: 53,888
  • Joined: 12-June 08

Posted 29 November 2016 - 07:46 AM

Right hand side -> mobile -> Android.
Was This Post Helpful? 1
  • +
  • -

#6 EndLessMind  Icon User is offline

  • Android Expert
  • member icon

Reputation: 271
  • View blog
  • Posts: 1,250
  • Joined: 13-March 09

Posted 29 November 2016 - 08:49 AM

@akshaypatil1234

As modi said, it's always a good idea to sketch down how you want the data structure/work flow and such to be.
You'll get a better view of what needs to be done and from there you can start checking stuff of the list.

Communication between the app and a server can be done i several different ways like PHP requests, javascript (like Websocket) or even a TCP/IP (like I'm using in this tutorial series).
It all depends on what kind of system you have at your disposal.

In my case, I've got my own Windows-base server running MySQL server and a Apache server, so I can basically use any of the above methods (and I do, depending on what I feel like using).
All the above mentioned methods can use a variety of different types of encryption. For example, if you're going to use PHP, you could use a secure server (HTTPS) and even have tokens being generated for each time you make a request, as well as your own "API key" system.

It all depends on your needs and what you have at your disposal.
Was This Post Helpful? 0
  • +
  • -

#7 akshaypatil1234  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 29-November 16

Posted 29 November 2016 - 09:17 AM

Wow! As expected you answered my question. The server MySQL & Apache you using are free or you have subscription for it? And as you said more secure https or API...so the one you are using aren't secure ??
Was This Post Helpful? 0
  • +
  • -

#8 EndLessMind  Icon User is offline

  • Android Expert
  • member icon

Reputation: 271
  • View blog
  • Posts: 1,250
  • Joined: 13-March 09

Posted 29 November 2016 - 09:52 AM

Hehe.

I'm using Vertrigo, it has PHP, Apache, MySQL, SQLite, Smarty, PhpMyAdmin and some other stuff and it runs on Windows.
I'm currently not using HTTPS, but you can configure Apache to use SSL for HTTPS, I just haven't done it for my server yet.
As I'm currently moving some projects over to more, hm, efficient socket handling (very much like the one I use in this tutorial series)
I'll also start using HTTPS for the projects that uses PHP for the database communications.

I'm guessing that you're wondering why I haven't used HTTPS before.
Well, the thing is when you make it a habit of using some form av encryption, you'll get stuck in it.
That's generally a good thing, but when writing tutorials and helping people on forums and such, you do want your posts to be
simple to understand and leave the users with options for improvements. Having habits of doing things in a more "advanced" way, might
break your thought process.
Was This Post Helpful? 0
  • +
  • -

#9 kevin.martin  Icon User is offline

  • New D.I.C Head

Reputation: -1
  • View blog
  • Posts: 6
  • Joined: 29-November 16

Posted 01 December 2016 - 06:27 AM

This was really very interesting and knowledgeable session. waiting for part 2.
Was This Post Helpful? 0
  • +
  • -

#10 EndLessMind  Icon User is offline

  • Android Expert
  • member icon

Reputation: 271
  • View blog
  • Posts: 1,250
  • Joined: 13-March 09

Posted 01 December 2016 - 06:30 AM

View Postkevin.martin, on 01 December 2016 - 01:27 PM, said:

This was really very interesting and knowledgeable session. waiting for part 2.



Thanks!

Part 2 is already out. You can find it here
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1