In this tutorial we will make a TCP Connection. The server will be written in Java and the client will be written in Android. Actually it will be a very simple messenger client.
Note: Now we have an enhanced version of TCP Connection here, but it’s just the code. For more details and explanations you still have to read the current tutorial. :)
1. Make the Server in Java
To create the server create a new project in Java and make a class called “TCPServer”. In this class put the following code:
UPDATED
import javax.swing.*; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * The class extends the Thread class so we can receive and send messages at the same time */ public class TCPServer extends Thread { public static final int SERVERPORT = 4444; private boolean running = false; private PrintWriter mOut; private OnMessageReceived messageListener; public static void main(String[] args) { //opens the window where the messages will be received and sent ServerBoard frame = new ServerBoard(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } /** * Constructor of the class * @param messageListener listens for the messages */ public TCPServer(OnMessageReceived messageListener) { this.messageListener = messageListener; } /** * Method to send the messages from server to client * @param message the message sent by the server */ public void sendMessage(String message){ if (mOut != null && !mOut.checkError()) { mOut.println(message); mOut.flush(); } } @Override public void run() { super.run(); running = true; try { System.out.println("S: Connecting..."); //create a server socket. A server socket waits for requests to come in over the network. ServerSocket serverSocket = new ServerSocket(SERVERPORT); //create client socket... the method accept() listens for a connection to be made to this socket and accepts it. Socket client = serverSocket.accept(); System.out.println("S: Receiving..."); try { //sends the message to the client mOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true); //read the message received from client BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); //in this while we wait to receive messages from client (it's an infinite loop) //this while it's like a listener for messages while (running) { String message = in.readLine(); if (message != null && messageListener != null) { //call the method messageReceived from ServerBoard class messageListener.messageReceived(message); } } } catch (Exception e) { System.out.println("S: Error"); e.printStackTrace(); } finally { client.close(); System.out.println("S: Done."); } } catch (Exception e) { System.out.println("S: Error"); e.printStackTrace(); } } //Declare the interface. The method messageReceived(String message) will must be implemented in the ServerBoard //class at on startServer button click public interface OnMessageReceived { public void messageReceived(String message); } }
UPDATE: NEW CLASS
Now create another java class and call it “ServerBoard”. In this class we will make the “messenger” UI (a very simple one) with a field where the messages will be added, a text field where the server side can enter a message and 2 buttons: one to start the connection and one to send the messages. The code is like this:
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class ServerBoard extends JFrame { private JTextArea messagesArea; private JButton sendButton; private JTextField message; private JButton startServer; private TCPServer mServer; public ServerBoard() { super("ServerBoard"); JPanel panelFields = new JPanel(); panelFields.setLayout(new BoxLayout(panelFields,BoxLayout.X_AXIS)); JPanel panelFields2 = new JPanel(); panelFields2.setLayout(new BoxLayout(panelFields2,BoxLayout.X_AXIS)); //here we will have the text messages screen messagesArea = new JTextArea(); messagesArea.setColumns(30); messagesArea.setRows(10); messagesArea.setEditable(false); sendButton = new JButton("Send"); sendButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // get the message from the text view String messageText = message.getText(); // add message to the message area messagesArea.append("\n" + messageText); // send the message to the client mServer.sendMessage(messageText); // clear text message.setText(""); } }); startServer = new JButton("Start"); startServer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // disable the start button startServer.setEnabled(false); //creates the object OnMessageReceived asked by the TCPServer constructor mServer = new TCPServer(new TCPServer.OnMessageReceived() { @Override //this method declared in the interface from TCPServer class is implemented here //this method is actually a callback method, because it will run every time when it will be called from //TCPServer class (at while) public void messageReceived(String message) { messagesArea.append("\n "+message); } }); mServer.start(); } }); //the box where the user enters the text (EditText is called in Android) message = new JTextField(); message.setSize(200, 20); //add the buttons and the text fields to the panel panelFields.add(messagesArea); panelFields.add(startServer); panelFields2.add(message); panelFields2.add(sendButton); getContentPane().add(panelFields); getContentPane().add(panelFields2); getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS)); setSize(300, 170); setVisible(true); } }
2. Create the Client in Android
- To create the Client, create a new project and call the class that creates with the project ” MyActivity”.
- After this make a new class and call it “TCPClient”
- Now in the class TCPClient put the following code:
import android.util.Log; import java.io.*; import java.net.InetAddress; import java.net.Socket; public class TCPClient { private String serverMessage; public static final String SERVERIP = "192.168.0.102"; //your computer IP address public static final int SERVERPORT = 4444; private OnMessageReceived mMessageListener = null; private boolean mRun = false; PrintWriter out; BufferedReader in; /** * Constructor of the class. OnMessagedReceived listens for the messages received from server */ public TCPClient(OnMessageReceived listener) { mMessageListener = listener; } /** * Sends the message entered by client to the server * @param message text entered by client */ public void sendMessage(String message){ if (out != null && !out.checkError()) { out.println(message); out.flush(); } } public void stopClient(){ mRun = false; } public void run() { mRun = true; try { //here you must put your computer's IP address. InetAddress serverAddr = InetAddress.getByName(SERVERIP); Log.e("TCP Client", "C: Connecting..."); //create a socket to make the connection with the server Socket socket = new Socket(serverAddr, SERVERPORT); try { //send the message to the server out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); Log.e("TCP Client", "C: Sent."); Log.e("TCP Client", "C: Done."); //receive the message which the server sends back in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //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.e("RESPONSE FROM SERVER", "S: Received Message: '" + serverMessage + "'"); } catch (Exception e) { Log.e("TCP", "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. 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); } }
At the beginning of this class you have a constant SERVERIP. In this constant you have to put your computer IPv4. To do this open the commander and type ipconfig and press Enter. Then you will see your computer’s IP.
Also the SERVERPORT from this class (TCPClient) must be the same with the SERVERPORT from server class (TCPServer).
Now that the server and client classes are done, we have to set the interface a little. For this we will make a ListView in MyActivity class where we will get the messages sent and received, an EditText to enter the message from client side and a button to send the message. So let’s begin.
- go to res – layout – main.xml and put this code in main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:transcriptMode="alwaysScroll" android:cacheColorHint="#00000000" android:listSelector="@android:color/transparent"/> <LinearLayout android:id="@+id/footer" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="bottom"> <EditText android:inputType="textMultiLine|textNoSuggestions" android:layout_width="0dp" android:layout_height="40dp" android:id="@+id/editText" android:layout_weight="1"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/send_button" android:layout_gravity="center_vertical" android:text="send" /> </LinearLayout> </LinearLayout>
- create a new xml file and call it “list_item.xml”. This will contain a TextView for the messages which will be added to the ListView. The code will look like this:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/list_item" android:gravity="center_vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/list_item_text_view" android:textSize="20sp" android:padding="10dp" android:layout_marginLeft="5dp"/> </LinearLayout>
- create a new class and call it “MyCustomAdapter”.
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import java.util.ArrayList; public class MyCustomAdapter extends BaseAdapter { private ArrayList<String> mListItems; private LayoutInflater mLayoutInflater; public MyCustomAdapter(Context context, ArrayList<String> arrayList){ mListItems = arrayList; //get the layout inflater mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public int getCount() { //getCount() represents how many items are in the list return mListItems.size(); } @Override //get the data of an item from a specific position //i represents the position of the item in the list public Object getItem(int i) { return null; } @Override //get the position id of the item from the list public long getItemId(int i) { return 0; } @Override public View getView(int position, View view, ViewGroup viewGroup) { //check to see if the reused view is null or not, if is not null then reuse it if (view == null) { view = mLayoutInflater.inflate(R.layout.list_item, null); } //get the string item from the position "position" from array list to put it on the TextView String stringItem = mListItems.get(position); if (stringItem != null) { TextView itemName = (TextView) view.findViewById(R.id.list_item_text_view); if (itemName != null) { //set the item name on the TextView itemName.setText(stringItem); } } //this method must return the view corresponding to the data at the specified position. return view; } }
UPDATED
- And now in the MyActivity class put the following code:
import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import java.util.ArrayList; public class MyActivity extends Activity { private ListView mList; private ArrayList<String> arrayList; private MyCustomAdapter mAdapter; private TCPClient mTcpClient; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); arrayList = new ArrayList<String>(); final EditText editText = (EditText) findViewById(R.id.editText); Button send = (Button)findViewById(R.id.send_button); //relate the listView from java to the one created in xml mList = (ListView)findViewById(R.id.list); mAdapter = new MyCustomAdapter(this, arrayList); mList.setAdapter(mAdapter); // connect to the server new connectTask().execute(""); send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String message = editText.getText().toString(); //add the text in the arrayList arrayList.add("c: " + message); //sends the message to the server if (mTcpClient != null) { mTcpClient.sendMessage(message); } //refresh the list mAdapter.notifyDataSetChanged(); editText.setText(""); } }); } public class connectTask extends AsyncTask<String,String,TCPClient> { @Override protected TCPClient doInBackground(String... message) { //we create a TCPClient object and mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() { @Override //here the messageReceived method is implemented public void messageReceived(String message) { //this method calls the onProgressUpdate publishProgress(message); } }); mTcpClient.run(); return null; } @Override protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); //in the arrayList we add the messaged received from server arrayList.add(values[0]); // notify the adapter that the data set has changed. This means that new message received // from server was added to the list mAdapter.notifyDataSetChanged(); } } }
- And one last thing. Go to AndroidManifest.xml and put the internet permission just before the :
<uses-permission android:name="android.permission.INTERNET" />
Now that the project is done we must see how it actually works
1. First, you must open the server, so run the TCPServer class, the one that is made in Java and a window called ServerBoard will appear. Press on the start button like in the picture below. Do NOT start the android project before you press the start button!
2. The server waits the client to connect now, so run the android project on the emulator and wait for the project to load.