Advanced Java

Download as pdf or txt
Download as pdf or txt
You are on page 1of 50

This Advanced Java Book contains

Multithreading and Network Programming

Advanced
Java
SE

Dr. Yama Ramin


Chapter 1
Multithreading in Java
Multithreading is a Java feature that allows concurrent execution of two or more parts of a
program for maximum utilization of CPU. Each part of such program is called a thread. So, threads
are light-weight processes within a process.
However, we use multithreading than multiprocessing because threads use a shared memory
area. They don't allocate separate memory area so saves memory, and context-switching
between the threads takes less time than process.

Multitasking: Ability to execute more than one task at the same time is known as multitasking.
Multithreading: We already discussed about it. It is a process of executing multiple threads
simultaneously. Multithreading is also known as Thread-based Multitasking.
Multiprocessing: It is same as multitasking, however in multiprocessing more than one CPUs are
involved. On the other hand one CPU is involved in multitasking.
Parallel Processing: It refers to the utilization of multiple CPUs in a single computer system.

Multitasking can be achieved in two ways:


Process-based Multitasking (Multiprocessing)
Thread-based Multitasking (Multithreading)
1) Process-based Multitasking (Multiprocessing)

• Each process has an address in memory. In other words, each process allocates a
separate memory area.
• A process is heavyweight.
• Cost of communication between the process is high.
• Switching from one process to another requires some time for saving and loading
registers, memory maps, updating lists, etc.
2) Thread-based Multitasking (Multithreading)

• Threads share the same address space.


• A thread is lightweight.
• Cost of communication between the thread is low.

1
What is Thread in java?
A thread is a lightweight subprocess, the smallest unit of processing. It is a separate path of
execution.
Threads are independent. If there occurs exception in one thread, it doesn't affect other threads.
It uses a shared memory area.

As shown in the above figure, a thread is executed inside the process. There is context-switching
between the threads. There can be multiple processes inside the OS, and one process can have
multiple threads.
2
Life Cycle of a Thread
A thread goes through various stages in its life cycle. For example, a thread is born, started, runs,
and then dies. The following diagram shows the complete life cycle of a thread.

Following are the stages of the life cycle −

• New − A new thread begins its life cycle in the new state. It remains in this state until the
program starts the thread. It is also referred to as a born thread.
• Runnable − After a newly born thread is started, the thread becomes runnable. A thread
in this state is considered to be executing its task.
• Waiting − Sometimes, a thread transitions to the waiting state while the thread waits for
another thread to perform a task. A thread transitions back to the runnable state only
when another thread signals the waiting thread to continue executing.
• Timed Waiting − A runnable thread can enter the timed waiting state for a specified
interval of time. A thread in this state transitions back to the runnable state when that
time interval expires or when the event it is waiting for occurs.
• Terminated (Dead) − A runnable thread enters the terminated state when it completes
its task or otherwise terminates.

3
Creating a thread in Java
There are two ways to create a thread in Java:
1) By extending Thread class.
2) By implementing Runnable interface.

Before we begin with the programs(code) of creating threads, let’s have a look at these methods
of Thread class. We have used few of these methods in the example below.
1) getName(): It is used for Obtaining a thread’s name
2) getPriority(): Obtain a thread’s priority
3) isAlive(): Determine if a thread is still running
4) join(): Wait for a thread to terminate
5) run(): Entry point for the thread
6) sleep(): suspend a thread for a period of time
7) start(): start a thread by calling its run() method

Thread creation by extending the Thread class


We create a class that extends the java.lang.Thread class. This class overrides the run() method
available in the Thread class. A thread begins its life inside run() method. We create an object of
our new class and call start() method to start the execution of a thread. Start() invokes the run()
method on the Thread object.

// Java code for thread creation by extending


// the Thread class
class MultithreadingDemo extends Thread
{
public void run()
{
try
{

4
// Displaying the thread that is running
System.out.println ("Thread " +
Thread.currentThread().getId() +
" is running");

}
catch (Exception e)
{
// Throwing an exception
System.out.println ("Exception is caught");
}
}
}

// Main Class
public class Multithread
{
public static void main(String[] args)
{
int n = 8; // Number of threads
for (int i=0; i<8; i++)
{
MultithreadingDemo object = new MultithreadingDemo();
object.start();
}
}
}

5
Output :
Thread 8 is running
Thread 9 is running
Thread 10 is running
Thread 11 is running
Thread 12 is running
Thread 13 is running
Thread 14 is running
Thread 15 is running

Thread creation by implementing the Runnable Interface


We create a new class which implements java.lang.Runnable interface and override run()
method. Then we instantiate a Thread object and call start() method on this object.

// Java code for thread creation by implementing


// the Runnable Interface
class MultithreadingDemo implements Runnable
{
public void run()
{
try
{
// Displaying the thread that is running
System.out.println ("Thread " +
Thread.currentThread().getId() +

6
" is running");

}
catch (Exception e)
{
// Throwing an exception
System.out.println ("Exception is caught");
}
}
}

// Main Class
class Multithread
{
public static void main(String[] args)
{
int n = 8; // Number of threads
for (int i=0; i<8; i++)
{
Thread object = new Thread(new MultithreadingDemo());
object.start();
}
}
}

7
Output :

Thread 8 is running
Thread 9 is running
Thread 10 is running
Thread 11 is running
Thread 12 is running
Thread 13 is running
Thread 14 is running
Thread 15 is running

Thread Class vs Runnable Interface


1. If we extend the Thread class, our class cannot extend any other class because Java doesn’t
support multiple inheritance. But, if we implement the Runnable interface, our class can still
extend other base classes.
2. We can achieve basic functionality of a thread by extending Thread class because it provides
some inbuilt methods like yield(), interrupt() etc. that are not available in Runnable interface.

8
Can we start a thread twice in Java?
The answer is no, once a thread is started, it can never be started again. Doing so will throw an
IllegalThreadStateException. Lets have a look at the below code:

public class ThreadTwiceExample implements Runnable {


@Override
public void run(){
Thread t = Thread.currentThread();
System.out.println(t.getName()+" is executing.");

}
public static void main(String args[]){
Thread th1 = new Thread(new ThreadTwiceExample(), "th1");
th1.start();
th1.start();
}
}
Output:
Exception in thread "main" th1 is executing.
java.lang.IllegalThreadStateException
As you observe the first call to start() resulted in execution of run() method, however the
exception got thrown when we tried to call the start() second time.

9
Thread priorities
• Thread priorities are the integers which decide how one thread should be treated with
respect to the others.
• Thread priority decides when to switch from one running thread to another, process is
called context switching
• A thread can voluntarily release control and the highest priority thread that is ready to
run is given the CPU.
• A thread can be preempted by a higher priority thread no matter what the lower priority
thread is doing. Whenever a higher priority thread wants to run it does.
• To set the priority of the thread setPriority() method is used which is a method of the
class Thread Class.
• In place of defining the priority in integers, we can use MIN_PRIORITY, NORM_PRIORITY
or MAX_PRIORITY.

// Java program to demonstrate getPriority() and setPriority()


import java.lang.*;

class ThreadDemo extends Thread


{
public void run()
{
System.out.println("Inside run method");
}

public static void main(String[]args)


{
ThreadDemo t1 = new ThreadDemo();
ThreadDemo t2 = new ThreadDemo();
ThreadDemo t3 = new ThreadDemo();

10
System.out.println("t1 thread priority : " +
t1.getPriority()); // Default 5
System.out.println("t2 thread priority : " +
t2.getPriority()); // Default 5
System.out.println("t3 thread priority : " +
t3.getPriority()); // Default 5

t1.setPriority(2);
t2.setPriority(5);
t3.setPriority(8);

// t3.setPriority(21); will throw IllegalArgumentException


System.out.println("t1 thread priority : " +
t1.getPriority()); //2
System.out.println("t2 thread priority : " +
t2.getPriority()); //5
System.out.println("t3 thread priority : " +
t3.getPriority());//8

// Main thread
System.out.print(Thread.currentThread().getName());
System.out.println("Main thread priority : "
+ Thread.currentThread().getPriority());

// Main thread priority is set to 10


Thread.currentThread().setPriority(10);
System.out.println("Main thread priority : "

11
+ Thread.currentThread().getPriority());
}
}

Output:
t1 thread priority : 5
t2 thread priority : 5
t3 thread priority : 5
t1 thread priority : 2
t2 thread priority : 5
t3 thread priority : 8
Main thread priority : 5
Main thread priority : 10

Note:

• Thread with highest priority will get execution chance prior to other threads. Suppose
there are 3 threads t1, t2 and t3 with priorities 4, 6 and 1. So, thread t2 will execute first
based on maximum priority 6 after that t1 will execute and then t3.
• Default priority for main thread is always 5, it can be changed later. Default priority for all
other threads depends on the priority of parent thread.

// Java program to demonstrat ethat a child thread


// gets same priority as parent
import java.lang.*;

class ThreadDemo extends Thread


{
public void run()

12
{
System.out.println("Inside run method");
}
public static void main(String[]args)
{
// main thread priority is 6 now
Thread.currentThread().setPriority(6);

System.out.println("main thread priority : " +


Thread.currentThread().getPriority());

ThreadDemo t1 = new ThreadDemo();

// t1 thread is child of main thread


// so t1 thread will also have priority 6.
System.out.println("t1 thread priority : "
+ t1.getPriority());
}
}
Output:
Main thread priority : 6
t1 thread priority : 6

• If two threads have same priority then we can’t expect which thread will execute first. It
depends on thread scheduler’s algorithm(Round-Robin, First Come First Serve, etc)
• If we are using thread priority for thread scheduling then we should always keep in mind
that underlying platform should provide support for scheduling based on thread priority.

13
Methods: isAlive() and join()
• In all the practical situations main thread should finish last else other threads which have
spawned from the main thread will also finish.
• To know whether the thread has finished we can call isAlive() on the thread which returns
true if the thread is not finished.
• Another way to achieve this by using join() method, this method when called from the
parent thread makes parent thread wait till child thread terminates.
• These methods are defined in the Thread class.
• We have used isAlive() method in the above examples too.

Java Thread isAlive() method


The isAlive() method of thread class tests if the thread is alive. A thread is considered alive when
the start() method of thread class has been called and the thread is not yet dead. This method
returns true if the thread is still running and not finished.
public class JavaIsAliveExp extends Thread
{
public void run()
{
try
{
Thread.sleep(300);
System.out.println("is run() method isAlive "+Thread.currentThread().isAlive());
}
catch (InterruptedException ie) {
}
}
public static void main(String[] args)
{
JavaIsAliveExp t1 = new JavaIsAliveExp();

14
System.out.println("before starting thread isAlive: "+t1.isAlive());
t1.start();
System.out.println("after starting thread isAlive: "+t1.isAlive());
}
}
Output:
before starting thread isAlive: false
after starting thread isAlive: true
is run() method isAlive true

Java Thread join


Java Thread join method can be used to pause the current thread execution until unless the
specified thread is dead. There are three overloaded join functions.
public final void join(): This java thread join method puts the current thread on wait until the
thread on which it’s called is dead. If the thread is interrupted, it throws InterruptedException.
public final synchronized void join(long millis): This java thread join method is used to wait for
the thread on which it’s called to be dead or wait for specified milliseconds. Since thread
execution depends on OS implementation, it doesn’t guarantee that the current thread will wait
only for given time.
public final synchronized void join(long millis, int nanos): This java thread join method is used
to wait for thread to die for given milliseconds plus nanoseconds.

Here is a simple example showing usage of Thread join methods. The goal of the program is to
make sure main is the last thread to finish and third thread starts only when first one is dead.
public class ThreadJoinExample {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable(), "t1");
Thread t2 = new Thread(new MyRunnable(), "t2");

15
Thread t3 = new Thread(new MyRunnable(), "t3");
t1.start();
//start second thread after waiting for 2 seconds or if it's dead
try {
t1.join(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

t2.start();

//start third thread only when first thread is dead


try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

t3.start();

//let all threads finish execution before finishing main thread


try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block

16
e.printStackTrace();
}

System.out.println("All threads are dead, exiting main thread");


}

class MyRunnable implements Runnable{

@Override
public void run() {
System.out.println("Thread started:::"+Thread.currentThread().getName());
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread ended:::"+Thread.currentThread().getName());
}

Output of the above program is:


Thread started:::t1
Thread started:::t2
Thread ended:::t1

17
Thread started:::t3
Thread ended:::t2
Thread ended:::t3
All threads are dead, exiting main thread

Synchronization
• Multithreading introduces asynchronous behavior to the programs. If a thread is writing
some data another thread may be reading the same data at that time. This may bring
inconsistency.
• When two or more threads need access to a shared resource there should be some way
that the resource will be used only by one resource at a time. The process to achieve this
is called synchronization.
• To implement the synchronous behavior java has synchronous method. Once a thread is
inside a synchronized method, no other thread can call any other synchronized method
on the same object. All the other threads then wait until the first thread come out of the
synchronized block.
• When we want to synchronize access to objects of a class which was not designed for the
multithreaded access and the code of the method which needs to be accessed
synchronously is not available with us, in this case we cannot add the synchronized to the
appropriate methods. In java we have the solution for this, put the calls to the methods
(which needs to be synchronized) defined by this class inside a synchronized block in
following manner.

Java provides a way of creating threads and synchronizing their task by using synchronized blocks.
Synchronized blocks in Java are marked with the synchronized keyword. A synchronized block in
Java is synchronized on some object. All synchronized blocks synchronized on the same object
can only have one thread executing inside them at a time. All other threads attempting to enter
the synchronized block are blocked until the thread inside the synchronized block exits the block.

Following is the general form of a synchronized block:


// Only one thread can execute at a time.
// sync_object is a reference to an object

18
// whose lock associates with the monitor.
// The code is said to be synchronized on
// the monitor object
synchronized(sync_object)
{
// Access shared variables and other
// shared resources
}
This synchronization is implemented in Java with a concept called monitors. Only one thread can
own a monitor at a given time. When a thread acquires a lock, it is said to have entered the
monitor. All other threads attempting to enter the locked monitor will be suspended until the
first thread exits the monitor.
Following is an example of multi threading with synchronized.
// A Java program to demonstrate working of
// synchronized.
import java.io.*;
import java.util.*;

// A Class used to send a message


class Sender
{
public void send(String msg)
{
System.out.println("Sending\t" + msg );
try
{
Thread.sleep(1000);
}

19
catch (Exception e)
{
System.out.println("Thread interrupted.");
}
System.out.println("\n" + msg + "Sent");
}
}

// Class for send a message using Threads


class ThreadedSend extends Thread
{
private String msg;
Sender sender;

// Recieves a message object and a string


// message to be sent
ThreadedSend(String m, Sender obj)
{
msg = m;
sender = obj;
}

public void run()


{
// Only one thread can send a message
// at a time.
synchronized(sender)

20
{
// synchronizing the snd object
sender.send(msg);
}
}
}

// Driver class
class SyncDemo
{
public static void main(String args[])
{
Sender snd = new Sender();
ThreadedSend S1 =
new ThreadedSend( " Hi " , snd );
ThreadedSend S2 =
new ThreadedSend( " Bye " , snd );

// Start two threads of ThreadedSend type


S1.start();
S2.start();

// wait for threads to end


try
{
S1.join();
S2.join();

21
}
catch(Exception e)
{
System.out.println("Interrupted");
}
}
}
Output:
Sending Hi
Hi Sent
Sending Bye
Bye Sent

The output is same every-time we run the program.

22
Inter-thread Communication
We have few methods through which java threads can communicate with each other. These
methods are wait(), notify(), notifyAll(). All these methods can only be called from within a
synchronized method.
1) To understand synchronization java has a concept of monitor. Monitor can be thought of
as a box which can hold only one thread. Once a thread enters the monitor all the other
threads have to wait until that thread exits the monitor.
2) wait() tells the calling thread to give up the monitor and go to sleep until some other
thread enters the same monitor and calls notify().
3) notify() wakes up the first thread that called wait() on the same object.
notifyAll() wakes up all the threads that called wait() on the same object. The highest
priority thread will run first.

Let's see the simple example of inter thread communication.


class Customer{
int amount=10000;

synchronized void withdraw(int amount){


System.out.println("going to withdraw...");

if(this.amount<amount){
System.out.println("Less balance; waiting for deposit...");
try{wait();}catch(Exception e){}
}
this.amount-=amount;
System.out.println("withdraw completed...");
}

synchronized void deposit(int amount){

23
System.out.println("going to deposit...");
this.amount+=amount;
System.out.println("deposit completed... ");
notify();
}
}

class Test{
public static void main(String args[]){
final Customer c=new Customer();
new Thread(){
public void run(){c.withdraw(15000);}
}.start();
new Thread(){
public void run(){c.deposit(10000);}
}.start();

}}

Output: going to withdraw...


Less balance; waiting for deposit...
going to deposit...
deposit completed...
withdraw completed

24
Interrupting a Thread:
If any thread is in sleeping or waiting state (i.e. sleep() or wait() is invoked), calling the interrupt()
method on the thread, breaks out the sleeping or waiting state throwing InterruptedException.
If the thread is not in the sleeping or waiting state, calling the interrupt() method performs
normal behaviour and doesn't interrupt the thread but sets the interrupt flag to true. Let's first
see the methods provided by the Thread class for thread interruption.
In this example, after interrupting the thread, we are propagating it, so it will stop working. If we
don't want to stop the thread, we can handle it where sleep() or wait() method is invoked.
Let's first see the example where we are propagating the exception.
class TestInterruptingThread1 extends Thread{
public void run(){
try{
Thread.sleep(1000);
System.out.println("task");
}catch(InterruptedException e){
throw new RuntimeException("Thread interrupted..."+e);
}

public static void main(String args[]){


TestInterruptingThread1 t1=new TestInterruptingThread1();
t1.start();
try{
t1.interrupt();
}catch(Exception e){System.out.println("Exception handled "+e);}

}
}

25
Output:Exception in thread-0
java.lang.RuntimeException: Thread interrupted...
java.lang.InterruptedException: sleep interrupted
at A.run(A.java:7)

In this example, after interrupting the thread, we handle the exception, so it will break out the
sleeping but will not stop working.
class TestInterruptingThread2 extends Thread{
public void run(){
try{
Thread.sleep(1000);
System.out.println("task");
}catch(InterruptedException e){
System.out.println("Exception handled "+e);
}
System.out.println("thread is running...");
}

public static void main(String args[]){


TestInterruptingThread2 t1=new TestInterruptingThread2();
t1.start();

t1.interrupt();

}
}

26
Output:Exception handled
java.lang.InterruptedException: sleep interrupted
thread is running...

If thread is not in sleeping or waiting state, calling the interrupt() method sets the interrupted
flag to true that can be used to stop the thread by the java programmer later.
class TestInterruptingThread3 extends Thread{
public void run(){
for(int i=1;i<=5;i++)
System.out.println(i);
}

public static void main(String args[]){


TestInterruptingThread3 t1=new TestInterruptingThread3();
t1.start();

t1.interrupt();

}
}

Output:1
2
3
4
5

27
Thread Deadlock
Deadlock describes a situation where two or more threads are blocked forever, waiting for each
other. Deadlock occurs when multiple threads need the same locks but obtain them in different
order. A Java multithreaded program may suffer from the deadlock condition because the
synchronized keyword causes the executing thread to block while waiting for the lock, or
monitor, associated with the specified object. Here is an example.

public class TestThread {


public static Object Lock1 = new Object();
public static Object Lock2 = new Object();

public static void main(String args[]) {


ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}

private static class ThreadDemo1 extends Thread {


public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");

try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");

synchronized (Lock2) {

28
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");

try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");

synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
When you compile and execute the above program, you find a deadlock situation and following
is the output produced by the program −
Output
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...

29
Thread 2: Waiting for lock 1...
The above program will hang forever because neither of the threads in position to proceed and
waiting for each other to release the lock, so you can come out of the program by pressing
CTRL+C.

Deadlock Solution Example


Let's change the order of the lock and run of the same program to see if both the threads still
wait for each other −

public class TestThread {


public static Object Lock1 = new Object();
public static Object Lock2 = new Object();

public static void main(String args[]) {


ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}

private static class ThreadDemo1 extends Thread {


public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");

try {
Thread.sleep(10);
} catch (InterruptedException e) {}

30
System.out.println("Thread 1: Waiting for lock 2...");

synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1...");

try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 2...");

synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
So just changing the order of the locks prevent the program in going into a deadlock situation
and completes with the following result −

31
Output
Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

The above example is to just make the concept clear, however, it is a complex concept and you
should deep dive into it before you develop your applications to deal with deadlock situations.

32
Chapter 2

Java Network Programming

Basic Network Concepts


Network programming is no longer the province of a few specialists. It has become a core part of
every developer’s toolbox. Today, more programs are network aware than aren’t. Besides classic
applications like email, web browsers, and remote login, most major applications have some level
of networking built in.
Java was the first programming language designed from the ground up for network applications.
Java was originally aimed at proprietary cable television networks rather than the Internet, but
it’s always had the network foremost in mind. One of the first two real Java applications was a
web browser. As the Internet continues to grow, Java is uniquely suited to build the next
generation of network applications.

Networks
A network is a collection of computers and other devices that can send data to and receive data
from one another, more or less in real time. A network is often connected by wires, and the bits
of data are turned into electromagnetic waves that move through the wires. However, wireless
networks transmit data using radio waves; and most longdistance transmissions are now carried
over fiber-optic cables that send light waves through glass filaments.

The Layers of a Network


Sending data across a network is a complex operation that must be carefully tuned to the physical
characteristics of the network as well as the logical character of the data being sent. Software
that sends data across a network must understand how to avoid collisions between packets,
convert digital data to analog signals, detect and correct errors, route packets from one host to
another, and more. The process is further complicated when the requirement to support multiple
operating systems and heterogeneous network cabling is added.
To hide most of this complexity from the application developer and end user, the different
aspects of network communication are separated into multiple layers. Each layer represents a

33
different level of abstraction between the physical hardware (i.e., the wires and electricity) and
the information being transmitted. In theory, each layer only talks to the layers immediately
above and immediately below it. Separating the network into layers lets you modify or even
replace the software in one layer without affecting the others, as long as the interfaces between
the layers stay the same.

Protocols in different layers of a network

There are several different layer models, each organized to fit the needs of a particular kind of
network. This book uses the standard TCP/IP four-layer model appropriate for the Internet,
shown in Figure. In this model, applications like Firefox and Warcraft run in the application layer
and talk only to the transport layer. The transport layer talks only to the application layer and the
Internet layer. The Internet layer in turn talks only to the host-to-network layer and the transport
layer, never directly to the application layer. The host-to-network layer moves the data across
the wires, fiber-optic cables, or other medium to the host-to-network layer on the remote
system, which then moves the data up the layers to the application on the remote system.

34
The layers of a network

The structure of an IPv4 datagram

35
Layered connections through a proxy server

The Client/Server Model


Most modern network programming is based on a client/server model. A client/server
application typically stores large quantities of data on an expensive, high-powered server or cloud
of servers while most of the program logic and the user interface is handled by client software
running on relatively cheap personal computers. In most cases, a server primarily sends data
while a client primarily receives it; but it is rare for one program to send or receive exclusively. A
more reliable distinction is that a client initiates a conversation while a server waits for clients to
start conversations with it. Following Figure illustrates both possibilities. In some cases, the same
program may be both a client and a server.
You are already familiar with many examples of client/server systems. In 2013, the most popular
client/server system on the Internet is the Web. Web servers like Apache respond to requests
from web clients like Firefox. Data is stored on the web server and is sent out to the clients that
request it. Aside from the initial request for a page, almost all data is transferred from the server
to the client, not from the client to the server. FTP is an older service that fits the client/server
model. FTP uses different application protocols and different software, but is still split into FTP
servers that send files and FTP clients that receive files. People often use FTP to upload files from
the client to the server, so it’s harder to say that the data transfer is primarily in one direction,
but it is still true that an FTP client initiates the connection and the FTP server responds.

36
A client/server connection

37
Sockets for Clients
Data is transmitted across the Internet in packets of finite size called datagrams. Each datagram
contains a header and a payload. The header contains the address and port to which the packet
is going, the address and port from which the packet came, a checksum to detect data corruption,
and various other housekeeping information used to ensure reliable transmission. The payload
contains the data itself. However, because datagrams have a finite length, it’s often necessary to
split the data across multiple packets and reassemble it at the destination. It’s also possible that
one or more packets may be lost or corrupted in transit and need to be retransmitted or that
packets arrive out of order and need to be reordered. Keeping track of this—splitting the data
into packets, generating headers, parsing the headers of incoming packets, keeping track of what
packets have and haven’t been received, and so on—is a lot of work and requires a lot of intricate
code.
Fortunately, you don’t have to do the work yourself. Sockets allow the programmer to treat a
network connection as just another stream onto which bytes can be written and from which
bytes can be read. Sockets shield the programmer from low-level details of the network, such as
error detection, packet sizes, packet splitting, packet retransmission, network addresses, and
more.

Using Sockets
A socket is a connection between two hosts. It can perform seven basic operations:
• Connect to a remote machine
• Send data
• Receive data
• Close a connection
• Bind to a port
• Listen for incoming data
• Accept connections from remote machines on the bound port
Java’s Socket class, which is used by both clients and servers, has methods that correspond to
the first four of these operations. The last three operations are needed only by servers, which
wait for clients to connect to them. They are implemented by the ServerSocket class, which is
discussed in the next chapter. Java programs normally use client sockets in the following fashion:
• The program creates a new socket with a constructor.

38
• The socket attempts to connect to the remote host.
Once the connection is established, the local and remote hosts get input and output streams
from the socket and use those streams to send data to each other. This connection is full-duplex.
Both hosts can send and receive data simultaneously. What the data means depends on the
protocol; different commands are sent to an FTP server than to an HTTP server. There will
normally be some agreed-upon handshaking followed by the transmission of data from one to
the other.
When the transmission of data is complete, one or both sides close the connection. Some
protocols, such as HTTP 1.0, require the connection to be closed after each request is serviced.
Others, such as FTP and HTTP 1.1, allow multiple requests to be processed in a single connection.

A sample Messaging App - Client:

1 /*
2 * To change this license header, choose License Headers in Project Properties.
3 * To change this template file, choose Tools | Templates
4 * and open the template in the editor.
5 */
6 package client;
7
8 import java.awt.event.KeyEvent;
9 import java.io.DataInputStream;
10 import java.io.DataOutputStream;
11 import java.io.IOException;
12 import java.net.InetAddress;
13 import java.net.Socket;
14 import java.net.UnknownHostException;

39
15 import java.util.logging.Level;
16 import java.util.logging.Logger;
17
18 /**
19 *
20 * @author yamaramin
21 */
22 public class NewJFrame extends javax.swing.JFrame {
23
24 /**
25 * Creates new form NewJFrame
26 */
27 public NewJFrame() {
28 initComponents();
29 }
30
31 /**
32 * This method is called from within the constructor to initialize the form.
33 * WARNING: Do NOT modify this code. The content of this method is always
34 * regenerated by the Form Editor.
35 */
36 @SuppressWarnings("unchecked")
37 // <editor-fold defaultstate="collapsed" desc="Generated Code">
38 private void initComponents() {
39
40 jScrollPane1 = new javax.swing.JScrollPane();
41 jTextArea1 = new javax.swing.JTextArea();
42 jTextField1 = new javax.swing.JTextField();
43 jButton1 = new javax.swing.JButton();
44 jButton2 = new javax.swing.JButton();
45
46 setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
47
48 jTextArea1.setColumns(20);
49 jTextArea1.setRows(5);
50 jScrollPane1.setViewportView(jTextArea1);
51
52 jTextField1.addKeyListener(new java.awt.event.KeyAdapter() {
53 public void keyPressed(java.awt.event.KeyEvent evt) {
54 jTextField1KeyPressed(evt);
55 }
56 });
57
58 jButton1.setText("Send");
59 jButton1.addActionListener(new java.awt.event.ActionListener() {
60 public void actionPerformed(java.awt.event.ActionEvent evt) {
61 jButton1ActionPerformed(evt);
62 }
63 });
64
65 jButton2.setText("Connect");
66 jButton2.addActionListener(new java.awt.event.ActionListener() {
67 public void actionPerformed(java.awt.event.ActionEvent evt) {
68 jButton2ActionPerformed(evt);
69 }

40
70 });
71
72 javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
73 getContentPane().setLayout(layout);
74 layout.setHorizontalGroup(
75 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
76 .addComponent(jScrollPane1)
77 .addGroup(layout.createSequentialGroup()
78 .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 329,
javax.swing.GroupLayout.PREFERRED_SIZE)
79 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED,
javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
80 .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 81,
javax.swing.GroupLayout.PREFERRED_SIZE))
81 .addGroup(layout.createSequentialGroup()
82 .addContainerGap()
83 .addComponent(jButton2)
84 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
85 );
86 layout.setVerticalGroup(
87 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
88 .addGroup(layout.createSequentialGroup()
89 .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 214,
javax.swing.GroupLayout.PREFERRED_SIZE)
90 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
91 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
92 .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
93 .addComponent(jButton1))
94 .addGap(18, 18, 18)
95 .addComponent(jButton2)
96 .addGap(0, 16, Short.MAX_VALUE))
97 );
98
99 pack();
100 }// </editor-fold>
101 static Socket conn;
102 private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
103 try {
104 conn=new Socket(InetAddress.getLocalHost(), 6000);
105 jTextArea1.setText(InetAddress.getLocalHost().toString());
106 new Thread(){
107 @Override
108 public void run() {
109 while (true) {
110 try {
111 DataInputStream dis=new DataInputStream(conn.getInputStream());
112 String data=dis.readUTF();
113 jTextArea1.setText(jTextArea1.getText()+'\n'+"Server: "+data);
114 } catch (IOException ex) {
115 jTextArea1.setText(jTextArea1.getText()+'\n'+"Send failed! Network error. Exiting...");
116 try {
117 Thread.sleep(3000);
118 System.exit(0);
119 } catch (InterruptedException ex1) {

41
120 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex1);
121 }
122 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
123 }
124 }
125 }
126
127 }.start();
128 } catch (UnknownHostException ex) {
129 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
130 } catch (IOException ex) {
131 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
132 }
133
134 }
135
136 private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
137 if(jTextField1.getText()!=""){
138 jTextArea1.setText(jTextArea1.getText()+'\n'+"Client: "+jTextField1.getText());
139 try {
140 DataOutputStream dos=new DataOutputStream(conn.getOutputStream());
141 dos.writeUTF(jTextField1.getText());
142
143 } catch (IOException ex) {
144 jTextArea1.setText(jTextArea1.getText()+'\n'+"Send failed! Network error. Exiting...");
145 try {
146 Thread.sleep(3000);
147 System.exit(0);
148 } catch (InterruptedException ex1) {
149 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex1);
150 }
151 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
152 }
153 jTextField1.setText("");
154
155 }
156 }
157
158 private void jTextField1KeyPressed(java.awt.event.KeyEvent evt) {
159 if(evt.getKeyCode()==KeyEvent.VK_ENTER){
160 if(jTextField1.getText()!=""){
161 jTextArea1.setText(jTextArea1.getText()+'\n'+"Client: "+jTextField1.getText());
162 try {
163 DataOutputStream dos=new DataOutputStream(conn.getOutputStream());
164 dos.writeUTF(jTextField1.getText());
165
166 } catch (IOException ex) {
167 jTextArea1.setText(jTextArea1.getText()+'\n'+"Send failed! Network error. Exiting...");
168 try {
169 Thread.sleep(3000);
170 System.exit(0);
171 } catch (InterruptedException ex1) {
172 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex1);
173 }
174 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);

42
175 }
176 jTextField1.setText("");
177 }
178 }
179 }
180
181 /**
182 * @param args the command line arguments
183 */
184 public static void main(String args[]) {
185 /* Set the Nimbus look and feel */
186 //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
187 /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
188 * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
189 */
190 try {
191 for (javax.swing.UIManager.LookAndFeelInfo info :
javax.swing.UIManager.getInstalledLookAndFeels()) {
192 if ("Nimbus".equals(info.getName())) {
193 javax.swing.UIManager.setLookAndFeel(info.getClassName());
194 break;
195 }
196 }
197 } catch (ClassNotFoundException ex) {
198 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
199 } catch (InstantiationException ex) {
200 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
201 } catch (IllegalAccessException ex) {
202 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
203 } catch (javax.swing.UnsupportedLookAndFeelException ex) {
204 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
205 }
206 //</editor-fold>
207
208 /* Create and display the form */
209 java.awt.EventQueue.invokeLater(new Runnable() {
210 public void run() {
211 new NewJFrame().setVisible(true);
212 }
213 });
214 }
215
216 // Variables declaration - do not modify
217 private javax.swing.JButton jButton1;
218 private javax.swing.JButton jButton2;
219 private javax.swing.JScrollPane jScrollPane1;
220 private javax.swing.JTextArea jTextArea1;
221 private javax.swing.JTextField jTextField1;
222 // End of variables declaration
223 }

43
Sockets for Servers
The previous lesson discussed sockets from the standpoint of clients: programs that open a
socket to a server that’s listening for connections. However, client sockets themselves aren’t
enough; clients aren’t much use unless they can talk to a server, and the Socket class discussed
in the previous chapter is not sufficient for writing servers. To create a Socket, you need to know
the Internet host to which you want to connect.
When you’re writing a server, you don’t know in advance who will contact you; and even if you
did, you wouldn’t know when that host wanted to contact you. In other words, servers are like
receptionists who sit by the phone and wait for incoming calls. They don’t know who will call or
when, only that when the phone rings, they have to pick it up and talk to whoever is there. You
can’t program that behavior with the Socket class alone.
For servers that accept connections, Java provides a ServerSocket class that represents server
sockets. In essence, a server socket’s job is to sit by the phone and wait for incoming calls. More
technically, a server socket runs on the server and listens for incoming TCP connections. Each
server socket listens on a particular port on the server machine. When a client on a remote host
attempts to connect to that port, the server wakes up, negotiates the connection between the
client and the server, and returns a regular Socket object representing the socket between the
two hosts. In other words, server sockets wait for connections while client sockets initiate
connections. Once a ServerSocket has set up the connection, the server uses a regular Socket
object to send data to the client. Data always travels over the regular socket.

Using ServerSockets
The ServerSocket class contains everything needed to write servers in Java. It has constructors
that create new ServerSocket objects, methods that listen for connections on a specified port,
methods that configure the various server socket options, and the usual miscellaneous methods
such as toString().
In Java, the basic life cycle of a server program is this:
1. A new ServerSocket is created on a particular port using a ServerSocket() constructor.
2. The ServerSocket listens for incoming connection attempts on that port using its
accept() method. accept() blocks until a client attempts to make a connection, at which
point accept() returns a Socket object connecting the client and the server.
3. Depending on the type of server, either the Socket’s getInputStream() method,
getOutputStream() method, or both are called to get input and output streams
that communicate with the client.

44
4. The server and the client interact according to an agreed-upon protocol until it is time
to close the connection.
5. The server, the client, or both close the connection.
6. The server returns to step 2 and waits for the next connection.

A sample Messaging App - Server:

1 /*
2 * To change this license header, choose License Headers in Project Properties.
3 * To change this template file, choose Tools | Templates
4 * and open the template in the editor.
5 */
6 package server;
7
8 import java.io.DataInputStream;
9 import java.io.DataOutputStream;
10 import java.io.IOException;
11 import java.net.InetAddress;
12 import java.net.ServerSocket;
13 import java.net.Socket;
14 import java.net.UnknownHostException;
15 import java.util.logging.Level;
16 import java.util.logging.Logger;
17
18 /**
19 *
20 * @author yamaramin

45
21 */
22 public class NewJFrame extends javax.swing.JFrame {
23
24 /**
25 * Creates new form NewJFrame
26 */
27 public NewJFrame() {
28 initComponents();
29 }
30
31 /**
32 * This method is called from within the constructor to initialize the form.
33 * WARNING: Do NOT modify this code. The content of this method is always
34 * regenerated by the Form Editor.
35 */
36 @SuppressWarnings("unchecked")
37 // <editor-fold defaultstate="collapsed" desc="Generated Code">
38 private void initComponents() {
39
40 jScrollPane1 = new javax.swing.JScrollPane();
41 jTextArea1 = new javax.swing.JTextArea();
42 jTextField1 = new javax.swing.JTextField();
43 jButton1 = new javax.swing.JButton();
44 jButton2 = new javax.swing.JButton();
45
46 setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
47
48 jTextArea1.setColumns(20);
49 jTextArea1.setRows(5);
50 jScrollPane1.setViewportView(jTextArea1);
51
52 jButton1.setText("Send");
53 jButton1.addActionListener(new java.awt.event.ActionListener() {
54 public void actionPerformed(java.awt.event.ActionEvent evt) {
55 jButton1ActionPerformed(evt);
56 }
57 });
58
59 jButton2.setText("Start");
60 jButton2.addActionListener(new java.awt.event.ActionListener() {
61 public void actionPerformed(java.awt.event.ActionEvent evt) {
62 jButton2ActionPerformed(evt);
63 }
64 });
65
66 javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
67 getContentPane().setLayout(layout);
68 layout.setHorizontalGroup(
69 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
70 .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
71 .addGroup(layout.createSequentialGroup()
72 .addComponent(jTextField1)
73 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
74 .addComponent(jButton1))
75 .addGroup(layout.createSequentialGroup()

46
76 .addContainerGap()
77 .addComponent(jButton2)
78 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
79 );
80 layout.setVerticalGroup(
81 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
82 .addGroup(layout.createSequentialGroup()
83 .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 222,
javax.swing.GroupLayout.PREFERRED_SIZE)
84 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
85 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
86 .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
87 .addComponent(jButton1))
88 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
89 .addComponent(jButton2)
90 .addGap(0, 15, Short.MAX_VALUE))
91 );
92
93 pack();
94 }// </editor-fold>
95 static ServerSocket ss;
96 static Socket conn;
97 private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
98 try {
99 ss=new ServerSocket(6000, 1, InetAddress.getLocalHost());
100 conn=ss.accept();
101 new Thread(){
102 @Override
103 public void run() {
104 while(true){
105 try {
106 DataInputStream dis=new DataInputStream(conn.getInputStream());
107 String data=dis.readUTF();
108 jTextArea1.setText(jTextArea1.getText()+'\n'+"Client: "+data);
109 } catch (IOException ex) {
110 jTextArea1.setText(jTextArea1.getText()+'\n'+"Send failed! Network error. Exiting...");
111 try {
112 Thread.sleep(3000);
113 System.exit(0);
114 } catch (InterruptedException ex1) {
115 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex1);
116 }
117 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
118 }
119 }
120 }
121 }.start();
122 } catch (UnknownHostException ex) {
123 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
124 } catch (IOException ex) {
125 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
126 }
127
128 }

47
129
130 private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
131 if(jTextField1.getText()!=""){
132 jTextArea1.setText(jTextArea1.getText()+'\n'+"Server: "+jTextField1.getText());
133 try {
134 DataOutputStream dos=new DataOutputStream(conn.getOutputStream());
135 dos.writeUTF(jTextField1.getText());
136 } catch (IOException ex) {
137 jTextArea1.setText(jTextArea1.getText()+'\n'+"Send failed! Network error. Exiting...");
138 try {
139 Thread.sleep(3000);
140 System.exit(0);
141 } catch (InterruptedException ex1) {
142 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex1);
143 }
144 Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
145 }
146 jTextField1.setText("");
147
148 }
149 }
150
151 /**
152 * @param args the command line arguments
153 */
154 public static void main(String args[]) {
155 /* Set the Nimbus look and feel */
156 //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
157 /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
158 * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
159 */
160 try {
161 for (javax.swing.UIManager.LookAndFeelInfo info :
javax.swing.UIManager.getInstalledLookAndFeels()) {
162 if ("Nimbus".equals(info.getName())) {
163 javax.swing.UIManager.setLookAndFeel(info.getClassName());
164 break;
165 }
166 }
167 } catch (ClassNotFoundException ex) {
168 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
169 } catch (InstantiationException ex) {
170 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
171 } catch (IllegalAccessException ex) {
172 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
173 } catch (javax.swing.UnsupportedLookAndFeelException ex) {
174 java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
175 }
176 //</editor-fold>
177
178 /* Create and display the form */

48
179 java.awt.EventQueue.invokeLater(new Runnable() {
180 public void run() {
181 new NewJFrame().setVisible(true);
182 }
183 });
184 }
185
186 // Variables declaration - do not modify
187 private javax.swing.JButton jButton1;
188 private javax.swing.JButton jButton2;
189 private javax.swing.JScrollPane jScrollPane1;
190 private javax.swing.JTextArea jTextArea1;
191 private javax.swing.JTextField jTextField1;
192 // End of variables declaration
193 }

49

You might also like