Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learning Network Programming with Java

You're reading from   Learning Network Programming with Java Harness the hidden power of Java to build network-enabled applications with lower network traffic and faster processes

Arrow left icon
Product type Paperback
Published in Dec 2015
Publisher Packt
ISBN-13 9781785885471
Length 292 pages
Edition 1st Edition
Languages
Concepts
Arrow right icon
Author (1):
Arrow left icon
Richard M. Reese Richard M. Reese
Author Profile Icon Richard M. Reese
Richard M. Reese
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Getting Started with Network Programming FREE CHAPTER 2. Network Addressing 3. NIO Support for Networking 4. Client/Server Development 5. Peer-to-Peer Networks 6. UDP and Multicasting 7. Network Scalability 8. Network Security 9. Network Interoperability Index

Creating a simple echo server

We will start with the definition of the SimpleEchoServer class as shown next. In the main method, an initial server message will be displayed:

public class SimpleEchoServer {
    public static void main(String[] args) {
        System.out.println("Simple Echo Server");
        ...
    }
}

The remainder of the method's body consists of a series of try blocks to handle exceptions. In the first try block, a ServerSocket instance is created using 6000 as its parameter. The ServerSocket class is a specialized socket that is used by a server to listen for client requests. Its argument is its port number. The IP of the machine on which the server is located is not necessarily of interest to the server, but the client will ultimately need to know this IP address.

In the next code sequence, an instance of the ServerSocket class is created and its accept method is called. The ServerSocket will block this call until it receives a request from a client. Blocking means that the program is suspended until the method returns. When a request is received, the accept method will return a Socket class instance, which represents the connection between that client and the server. They can now send and receive messages:

    try (ServerSocket serverSocket = new ServerSocket(6000)){
        System.out.println("Waiting for connection.....");
        Socket clientSocket = serverSocket.accept();
        System.out.println("Connected to client");
         ...
    } catch (IOException ex) {
        // Handle exceptions
    }

After this client socket has been created, we can process the message sent to the server. As we are dealing with text, we will use a BufferedReader instance to read the message from the client. This is created using the client socket's getInputStream method. We will use a PrintWriter instance to reply to the client. This is created using the client socket's getOutputStream method, shown as follows:

    try (BufferedReader br = new BufferedReader(
                new InputStreamReader(
                clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(
                clientSocket.getOutputStream(), true)) {
        ...
        }
    }

The second argument to the PrintWriter constructor is set to true. This means that text sent using the out object will automatically be flushed after each use.

When text is written to a socket, it will sit in a buffer until either the buffer is full or a flush method is called. Performing automatic flushing saves us from having to remember to flush the buffer, but it can result in excessive flushing, whereas a single flush issued after the last write is performed, will also do.

The next code segment completes the server. The readLine method reads a line at a time from the client. This text is displayed and then sent back to the client using the out object:

    String inputLine;
    while ((inputLine = br.readLine()) != null) {
        System.out.println("Server: " + inputLine);
        out.println(inputLine);
    }

Before we demonstrate the server in action, we need to create a client application to use with it.

Creating a simple echo client

We start with the declaration of a SimpleEchoClient class where in the main method, a message is displayed indicating the application's start that is shown as follows:

public class SimpleEchoClient {
    public static void main(String args[]) {
        System.out.println("Simple Echo Client");
        ...
    }
}

A Socket instance needs to be created to connect to the server. In the following example, it is assumed that the server and the client are running on the same machine. The InetAddress class' static getLocalHost method returns this address, which is then used in the Socket class's constructor along with port 6000. If they are located on different machines, then the server's address needs to be used instead. As with the server, an instance of the PrintWriter and BufferedReader classes are created to allow text to be sent to and from the server:

    try {
        System.out.println("Waiting for connection.....");
        InetAddress localAddress = InetAddress.getLocalHost();

        try (Socket clientSocket = new Socket(localAddress, 6000);
                    PrintWriter out = new PrintWriter(
                        clientSocket.getOutputStream(), true);
                    BufferedReader br = new BufferedReader(
                        new InputStreamReader(
                        clientSocket.getInputStream()))) {
            ...
        }
    } catch (IOException ex) {
        // Handle exceptions
    }

Note

Localhost refers to the current machine. This has a specific IP address: 127.0.0.1. While a machine may be associated with an additional IP address, every machine can reach itself using this localhost address.

The user is then prompted to enter text. If the text is the quit command, then the infinite loop is terminated, and the application shuts down. Otherwise, the text is sent to the server using the out object. When the reply is returned, it is displayed as shown next:

    System.out.println("Connected to server");
    Scanner scanner = new Scanner(System.in);
    while (true) {
        System.out.print("Enter text: ");
        String inputLine = scanner.nextLine();
        if ("quit".equalsIgnoreCase(inputLine)) {
            break;
        }
        out.println(inputLine);
        String response = br.readLine();
        System.out.println("Server response: " + response);
    }

These programs can be implemented as two separate projects or within a single project. Either way, start the server first and then start the client. When the server starts, you will see the following displayed:

Simple Echo Server

Waiting for connection.....

When the client starts, you will see the following:

Simple Echo Client

Waiting for connection.....

Connected to server

Enter text:

Enter a message, and watch how the client and the server interact. The following is one possible series of input from the client's perspective:

Enter text: Hello server

Server response: Hello server

Enter text: Echo this!

Server response: Echo this!

Enter text: quit

The server's output is shown here after the client has entered the quit command:

Simple Echo Server

Waiting for connection.....

Connected to client

Client request: Hello server

Client request: Echo this!

This is one approach to implement the client and server. We will enhance this implementation in later chapters.

Using Java 8 to support the echo server and client

We will be providing examples of using many of the newer Java 8 features throughout this book. Here, we will show you alternative implementations of the previous echo server and client applications.

The server uses a while loop to process a client's request as duplicated here:

    String inputLine;
    while ((inputLine = br.readLine()) != null) {
        System.out.println("Client request: " + inputLine);
        out.println(inputLine);
    }

We can use the Supplier interface in conjunction with a Stream object to perform the same operation. The next statement uses a lambda expression to return a string from the client:

    Supplier<String> socketInput = () -> {
        try {
            return br.readLine();
        } catch (IOException ex) {
            return null;
        }
    };

An infinite stream is generated from the Supplier instance. The following map method gets input from the user and then sends it to the server. When quit is entered, the stream will terminate. The allMatch method is a short-circuit method, and when its argument evaluates to false, the stream is terminated:

    Stream<String> stream = Stream.generate(socketInput);
    stream
            .map(s -> {
                System.out.println("Client request: " + s);
                out.println(s);
                return s;
            })
            .allMatch(s -> s != null);

While this implementation is lengthier than the traditional implementation, it can provide more succinct and simple solutions to more complex problems.

On the client side, we can replace the while loop as duplicated here with a functional implementation:

    while (true) {
        System.out.print("Enter text: ");
        String inputLine = scanner.nextLine();
        if ("quit".equalsIgnoreCase(inputLine)) {
            break;
        }
        out.println(inputLine);

        String response = br.readLine();
        System.out.println("Server response: " + response);
    }

The functional solution also uses a Supplier instance to capture console input as shown here:

    Supplier<String> scannerInput = () -> scanner.next();

An infinite stream is generated, as shown next, with a map method providing the equivalent functionality:

    System.out.print("Enter text: ");
    Stream.generate(scannerInput)
        .map(s -> {
            out.println(s);
            System.out.println("Server response: " + s);
            System.out.print("Enter text: ");
            return s;
        })
        .allMatch(s -> !"quit".equalsIgnoreCase(s));

A functional approach is often a better solution to many problems.

Note that an additional prompt, Enter text:, was displayed on the client side after the quit command was entered. This is easily corrected by not displaying the prompt if the quit command was entered. This correction is left as an exercise for the reader.

You have been reading a chapter from
Learning Network Programming with Java
Published in: Dec 2015
Publisher: Packt
ISBN-13: 9781785885471
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image