Home > Java, Programming > Coffee time with Java – Part 1 – Sockets

Coffee time with Java – Part 1 – Sockets

Good evening.
For the first topic of my blog (or, to be more accurate, group of topics) I’ve chosen Java, since I use it a lot lately due to my university education. The following guide is written rather for Java newcomers, and it doesn’t bring anything new into existing guides, manuals and other information around the Web, though it is pretty clear and straightforward, and concerns different issues in Java such as sockets, threading, designing GUI and possibly J2ME platform. So, if you are ready, turn on your Eclipse, or NetBeans, or whatever you use and we will begin.

Application: %application_name%
Platform: J2SE
Required software: JVM 1.6.0, JDK

Goal: to write a primitive server-client application in a short time
Covered issues: Sockets

For the beginning, lets understand what the socket is. Internet socket (or network socket) is an endpoint of a connection between two nodes in some network. You may consider a socket as kind of a plug or vice versa an outlet.
Sockets are of two different types, to connect and to be connected to, client sockets and server sockets respectively. In order to create a connection between two nodes, client socket sends a request to server socket. If server socket accepts it and catches the incoming socket, the connection is established and data can be transfered via the input and output streams of sockets.


Java supports sockets through classes in java.net package. java.net. Socket represents a client socket, and java.net.ServerSocket is used as server socket.
So, let’s finally write some code! We will start with the server part. Create a new package called “server” in your project, and then create a class Server inside. First, add to the class these two lines:


import java.io.*;
import java.net.*;

These packages contain different classes and exceptions we will need in our project. Now we should declare local fields:


private static ServerSocket serverSocket; //socket that will act as an "entry point" to our server
private static Socket clientSocket; //socket that we will use to communicate with connected client
private static PrintWriter output;
private static BufferedReader input;

Next, we need a main method that creates a server socket listening for incoming connection. When a client connects, server will create its own socket – another end of connection. Let’s see the code:


        try {
            serverSocket = new ServerSocket(4321); //Creates a new server socket listening port 4321. You can specify here any unused port
        } catch (IOException e) {
            System.err.println("Creating a server socket on port 4321 failed");
            System.exit(1);
        }

        try {
            clientSocket = serverSocket.accept();
            output = new PrintWriter(clientSocket.getOutputStream(), true);
            input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        } catch (IOException e) {
            System.err.println("Cannot accept incoming connection");
            System.exit(1);
        }

Look carefully at the accept() method. It activates only when a client tries to connect to server. Till that time the program execution stays on pause. PrintWriter and BufferedReader objects are wrappers around input and output streams, through which we will recieve and transmit data.
We haven’t still decided what our application will do. For the sake of simplicity, let it be primitive storage server that keeps pairs key-value and provides three actions: GET, PUT and DELETE. Later on you can extend its functionality by saving data to the file or encrypting it, but by now we won’t do that. Hence append this code to the main method:


        String inputString, key;
        HashMap<String,String> dictionary = new HashMap<String,String>();
        try {
            while ((inputString = input.readLine()) != null) { //if client passes something to us
                System.out.println(inputString);
                StringTokenizer tokenizer = new StringTokenizer(inputString," "); //create tokenizer to split input message
                switch (getAction(tokenizer.nextToken())) {
                    case 0: { //if request starts with GET, we response with requested value
                        key = tokenizer.nextToken();
                        if (dictionary.containsKey(key))
                            output.println(key+" = "+dictionary.get(key));
                        else
                            output.println("No such key found");
                        break;
                        }
                    case 1: { //if request starts with PUT, we add key-value pair to our hashmap
                        key = tokenizer.nextToken();
                        String value = inputString.substring(5+key.length(),inputString.length());
                        dictionary.put(key,value);
                        output.println("Value of "+key+" added");
                        break;
                    }
                    case 2: { //if request starts with DELETE, we delete the according value
                        key = tokenizer.nextToken();
                        if (dictionary.containsKey(key)) {
                            dictionary.remove(key);
                            output.println("Value of "+key+" deleted");
                        }
                        else
                            output.println("No such key found");
                        break;
                    }
                    default: output.println("Unknown request");
                }
            }
        }
        catch (IOException e) {System.err.println("Input/output error occurred"); }
    }

    private static int getAction(String action) {
        if (action.equalsIgnoreCase("GET"))
            return 0;
        if (action.equalsIgnoreCase("PUT"))
            return 1;
        if (action.equalsIgnoreCase("DELETE"))
            return 2;
        return -1;
    }

This code looks a bit massive, but it is actually quite simple. When the client send us a request, we divide it into parts with StringTokenizer, by the first token we decide what particular command we suppose to do, and depending on that we perform operations and return the appropriate result to the client.
Congratulations, the server is ready to use! But we can’t use it without a client, so let’s get back to work.
Create a package named “client” and a class Client inside. Our client’s code would be even simpler and somewhere similar to server’s one. It would have only main method.


    public static void main(String[] args) throws IOException {
        Socket socket = null;
        PrintWriter output = null;
        BufferedReader serverInput = null;

        try {
            socket = new Socket("localhost", 4321);
            output = new PrintWriter(socket.getOutputStream(), true);
            serverInput = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Unknown host");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Cannot connect to server");
            System.exit(1);
        };

As you can see, we create our working socket using a constructor with server IP and port as parameters. Since we want to test our application on our computer first, we specify “localhost” as an address.


        BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in)); //wrapper around the keyboard input stream
        String request;
        while ((request = userInput.readLine()) != null) { //waiting until user enters the request with keyboard
	    output.println(request); //send this request to the server
	    System.out.println(serverInput.readLine()); //get the response from server and show it on the screen
        }
        
	output.close(); //these four lines are just a good style - clean up everything after the work
        serverInput.close();
        userInput.close();
        socket.close();

And that’s it! Our client-server combination is complete. You can test it running the server, and then the client, and try to input something into client’s console. Here is the log I got:

PAT message Hello world!
Unknown request
PUT message Hello world!
Value of message added
GET message
message = Hello world!
DELETE message
Value of message deleted
GET message
No such key found

I want to stop here for now and thank you for your patience. This application is really simple and minimalistic, but you can extend it how much you’d like. Actually, in our next lesson I’ll tell you how to teach our server to handle more than one client simultaneously using threads. Considering that, see you next time!

Advertisements
Categories: Java, Programming Tags: , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: