Lab 7
Lab 7
Lab 7
Socket Programming
Objective:
The objective of this lab is to familiarize the students with the socket programming.
Activity Outcomes:
After this lab the students will
• Have basic knowledge of socket programming. The students will understand the basic concepts
of connection-oriented (TCP) and connection-less (UDP) communication using Java.
• Be able to write Java programs to write network applications, such as client-server application
for chat.
• To use threads in network programming. The reason is simple, we don’t want only a single client
to connect to server at a particular time but many clients simultaneously. We want our architecture
to support multiple clients at the same time.
Instructor Note:
Transport protocols are used to deliver information from one port to another and thereby enable
communication between application programs. They use either a connection-oriented or
connectionless method of communication. TCP is a connection-oriented protocol, and UDP is a
connectionless transport protocol. The TCP connection-oriented protocol establishes a
communication link between a source port/IP address and a destination port/IP address. The ports
79
are bound together via this link until the connection is terminated and the link is broken. An
example of a connection-oriented protocol is a telephone conversation. A telephone connection is
established, communication takes place, and then the connection is terminated. The reliability of
the communication between the source and destination programs is ensured through error-
detection and error-correction mechanisms that are implemented within TCP. TCP implements the
connection as a stream of bytes from source to destination. This feature allows the use of the stream
I/O classes provided by java.io. The UDP connectionless protocol differs from the TCP
connection-oriented protocol in that it does not establish a link for the duration of the connection.
An example of a connectionless protocol is postal mail. To mail something, you just write down a
destination address (and an optional return address) on the envelope of the item you're sending and
drop it into a mailbox. When using UDP, an application program writes the destination port and
IP address on a datagram and then sends the datagram to its destination. UDP is less reliable than
TCP because there are no delivery-assurance or error-detection-and-correction mechanisms built
into the protocol.
Application protocols such as FTP, SMTP, and HTTP use TCP to provide reliable, stream-based
communication between client and server programs. Other protocols, such as the Time Protocol,
use UDP because speed of delivery is more important than end-to- end reliability.
In this lab, you will learn how to code client/server applications based on UDP and TCP protocols.
For more details, please check the below reference.
Reference:
Jim Kurose, Keith Ross, "Computer Networking: A Top-Down Approach," 8th edition,
Pearson, 2020.
Java Network Programming, by Elliotte Rusty Harold (O'Reilly)
Java Network Programming, by Merlin and Conrad Hughes, Michael Shoffner, and Maria
Winslow (Manning, an imprint of Prentice-Hall)
Advanced Java Networking, by Prashant Sridharan (Prentice-Hall)
https://www.ibm.com/docs/en/i/7.3?topic=design-creating-connectionless-socket
https://www.javatpoint.com/DatagramSocket-and-DatagramPacket
80
1. Solved Lab Activites
Sr.No Allocated Time Level of Complexity CLO Mapping
1 15 Low CLO-5
2 10 Medium CLO-5
3 15 Medium CLO-5
4 20 High CLO-5
5 15 Low CLO-5
81
•close(). Tear down local communication resources. After this method has been
called, release this object.
import java.io.*;
import java.net.*;
public class UdpServer
{
public static void main(String args[]) throws Exception
{
// open datagram socket on port 9876
DatagramSocket sock = new DatagramSocket(9876);
// create two packets sharing a common buffer
byte[] buf = new byte[1000];
DatagramPacket inPkt = new DatagramPacket(buf, buf.length);
DatagramPacket outPkt = new DatagramPacket(buf, buf.length);
while (true)
{
// wait for incoming packet
sock.receive(inPkt);
// set address, port and length fields of outPkt
// so as to return contents of inPkt to sender
outPkt.setAddress(inPkt.getAddress());
outPkt.setPort(inPkt.getPort());
outPkt.setLength(inPkt.getLength());
// and send it back
sock.send(outPkt);
}
}
}
82
And the following code is for a client program that simply connects to the server, takes input from
the user, sends it to server. Upon receiving the text from server prints it.
83
returns the address of the host to which the socket is connected.
The getLocalPort() method returns the port on which the server socket listens for an incoming
connection.
The following program demonstrates how to implement a simple server that returns the current
date time for every new client. Here’s the code:
import java.io.*;
import java.net.*;
import java.util.Date;
/**
* This program demonstrates a simple TCP/IP socket server.
*
*/
public class TimeServer {
while (true) {
Socket socket = serverSocket.accept();
System.out.println("New client connected");
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(output, true);
writer.println(new Date().toString());
}
You need to specify a port number when running this server program, for example:
java TimeServer 6868
This makes the server listens for client requests on the port number 6868. You would see the
server’s output:
Server is listening on port 6868
And the following code is for a client program that simply connects to the server and prints the
data received, and then terminates:
import java.net.*;
import java.io.*;
/**
* This program demonstrates a simple TCP/IP socket client.
*
*/
public class TimeClient {
85
To run this client program, you have to specify the hostname/IP address and port number of the
server. If the client is on the same computer with the server, type the following command to run
it:
java TimeClient localhost 6868
Then you see a new output in the server program indicating that the client is connected:
New client connected
And you should see the client’s output:
Mon May 13 11:00:31 ICT 2022
This is the date time information returned from the server. Then the client terminates and the
server is still running, waiting for new connections. It’s that simple.
Multithreading
Learning Objectives:
Thread Basics
A thread is a flow of control through a program. Unlike a process, a thread does not have a separate
allocation of memory but shares memory with other threads created by the same application. This
means that servers using threads do not exhaust their supply of available memory and collapse
under the weight of excessive demand from clients, as they were prone to do when creating many
86
separate processes. In addition, the threads created by an application can share global variables,
which is often highly desirable. This does not prevent each thread from having its own local
variables, of course, since it will still have its own stack for such variables.
Though it has been entirely transparent to us and we have had to make no explicit programming
allowance for it, we have already been making use of threads in our Java programming. In fact,
we cannot avoid using threads in Java, since each program will have at least one thread that is
launched automatically by our machine’s JVM when that program is executed. Such a thread is
created when main is started and ‘killed’ when main terminates. If we wish to make use of further
threads, in order to ‘offload’ processing tasks onto them, then we have to program such threads
explicitly. Using more than one thread in this way is called multithreading.
The second of these provides a name for the thread via its argument. If the first is used, the system
generates a name of the form Thread-n, where n is an integer starting at zero and increasing in
value for further threads. Thus, if three threads are created via the first constructor, they will have
names Thread-0, Thread-1 and Thread-2 respectively. Whichever constructor is used, method
getName may be used to retrieve the name.
Example
Note that the name of the variable holding the address of a thread is not the same as the name of
the thread! More often than not, however, we do not need to know the latter.
Method sleep is used to make a thread pause for a specified number of milliseconds. For example:
This suspends execution of the thread and allows other threads to be executed. When the sleeping
time expires, the sleeping thread returns to a ready state, waiting for the processor.
Method interrupt may be used to interrupt an individual thread. In particular, this method may be
used by other threads to ‘awaken’ a sleeping thread before that thread’s sleeping time has expired.
Since method sleep will throw a checked exception (an InterruptedException) if another thread
invokes the interrupt method, it must be called from within a try block that catches this exception.
In the next example, static method random from core class Math is used to generate a random
sleeping time for each of two threads that simply display their own names ten times. If we were to
run the program without using a randomising element, then it would simply display alternating
names, which would be pretty tedious and would give no indication that threads were being used.
Method random returns a random decimal value in the range 0-0.999…, which is then multiplied
88
by a scaling factor of 3000 and typecast into an int, producing a final integer value in the range 0-
2999. This randomising technique is also used in later thread examples, again in order to avoid
producing the same pattern of output from a given program.
Activity 3:
Note the use of extends Thread in the opening line of the class. Though this class already
implements the Runnable interface (and so has a definition of method run), the default
implementation of run does nothing and must be overridden by a definition that we supply.
Example
Activity No 4:
In this example, we shall again create two threads, but we shall have one thread display the
message ‘Hello’ five times and the other thread output integers 1-5. For the first thread, we shall
create a class called HelloThread; for the second, we shall create class CountThread. Note that it
is not the main application class (ThreadHelloCount, here) that extends class Thread this time, but
each of the two subordinate classes, HelloThread and CountThread. Each has its own version of
the run method.
90
public static void main(String[] args)
{
HelloThread hello = new HelloThread();
CountThread count = new CountThread();
hello.start();
count.start();
}
}
91
{
for (int i=0; i<5; i++)
{
try
{
System.out.println(i); pause=(int)(Math.random()*3000); sleep (pause);
92
Multithreaded Servers
There is a fundamental and important limitation associated with all the server programs
encountered so far:
• they can handle only one connection at a time.
This restriction is simply not feasible for most real-world applications and would render the
software useless. There are two possible solutions:
• use a non-blocking server;
• use a multithreaded server.
Though inferior to the non-blocking approach, the multithreaded technique has a couple of
significant benefits:
• it offers a 'clean' implementation, by separating the task of
allocating connections from that of processing each connection;
• it is robust, since a problem with one connection will not affect
other connections.
Since each thread is responsible for handling all further dialogue with its particular client, the main
thread can 'forget' about the client once a thread has been allocated to it. It can then concentrate on
its simple tasks of waiting for clients to make connection and allocating threads to them as they do
so. For each client-handling thread that is created, of course, the main thread must ensure that the
client-handling thread is passed a reference to the socket that was opened for the associated client.
The separation of responsibilities means that, if a problem occurs with the connection to a
particular client, it has no effect on the connections to other clients and there is no general loss of
service. This is a major benefit, of course.
93
Activity No 5:
This is another echo server implementation, but one that uses multithreading to return messages to
multiple clients. It makes use of a support class called ClientHandler that extends class Thread.
Whenever a new client makes connection, a ClientHandler thread is created to handle all
subsequent communication with that particular client. When the ClientHandler thread is created,
its constructor is supplied with a reference to the relevant socket.
Here's the code for the server...
import java.io.*;
import java.net.*;
}
do
{
//Wait for client...
Socket client = serverSocket.accept();
System.out.println("\nNew client accepted.\n");
//Create a thread to handle communication with //this client and pass the constructor for this
//thread a reference to the relevant socket...
94
ClientHandler handler =
new ClientHandler(client); handler.start();//As usual, method calls run.
}
while (true);
}
}
95
//Echo message back to client on //the socket's output stream...
output.println("ECHO: " + received);
//Repeat above until 'QUIT' sent by client...
}
while (!received.equals("QUIT"));
try
{
if (client!=null)
{
System.out.println( "Closing down connection...");
client.close();
}
}
catch(IOException ioEx)
{
System.out.println("Unable to disconnect!");
}
}
}
The code required for the client program is exactly that which was employed in the TCPEchoClient
program from the last chapter. However, since (i) there was only a modest amount of code in the
run method for that program, (ii) we should avoid confusion with the run method of the Thread
class and (iii) it'll make a change (!) without being harmful, all the executable code has been placed
inside main in the MultiEchoClient program below.
Activity No 6:
import java.io.*;
import java.net.*;
import java.util.*;
public class MultiEchoClient {
private static InetAddress host; private static final int PORT = 1234;
public static void main(String[] args)
96
{
try
{
host = InetAddress.getLocalHost();
}
catch(UnknownHostException uhEx)
{
System.out.println("\nHost ID not found!\n");
System.exit(1);
}
sendMessages();
}
private static void sendMessages()
{
Socket socket = null;
try
{
socket = new Socket(host,PORT);
Scanner networkInput = new Scanner(socket.getInputStream());
PrintWriter networkOutput = new PrintWriter( socket.getOutputStream(),true);
//Set up stream for keyboard entry...
97
System.out.println( "\nSERVER> " + response);
}
while (!message.equals("QUIT"));
}
catch(IOException ioEx)
{
ioEx.printStackTrace();
}
finally
{
try
{
System.out.println( "\nClosing connection...");
socket.close();
}
catch(IOException ioEx)
{
System.out.println( "Unable to disconnect!");
System.exit(1);
}
}
}
}
98
Activity 7: TCP-based client/server program where the Server (single-threaded) is responsible
for reversing the text sent by client.
The following TCP-based server program echoes anything sent from the client in reversed form
(hence the name ReverseServer). Here’s the code:
import java.io.*;
import java.net.*;
/**
* The server program echoes anything sent from the client in reversed form
* This server is single-threaded.
*
*/
public class ReverseServer {
while (true) {
Socket socket = serverSocket.accept();
System.out.println("New client connected");
do {
text = reader.readLine();
99
String reverseText = new StringBuilder(text).reverse().toString();
writer.println("Server: " + reverseText);
} while (!text.equals("bye"));
socket.close();
}
The server is up and running, waiting for incoming requests from clients:
Now, let’s create a client program. The following program connects to the server, reads input
from the user and prints the response from the server. Here’s the code:
import java.net.*;
import java.io.*;
/**
* This program demonstrates a simple TCP/IP socket client that reads input
* from the user and prints echoed message from the server.
*
*/
public class ReverseClient {
100
String hostname = args[0];
int port = Integer.parseInt(args[1]);
try (Socket socket = new Socket(hostname, port)) {
do {
text = console.readLine("Enter text: ");
writer.println(text);
System.out.println(time);
} while (!text.equals("bye"));
socket.close();
Type something, say ‘Hello’ and you should see the server’s response like this:
Enter text: Hello
Server: olleH
Enter text:_
Keep this first client program running, and start a new one. In the second client program, you
will see it asks for input and then hangs forever. Why?
It’s because the server is single-threaded, and while it is busily serving the first client,
subsequent clients are block.
Modify the server’s code to handle each socket client in a new thread like this:
import java.io.*;
import java.net.*;
/**
* This program demonstrates a simple TCP/IP socket server that echoes every
* message from the client in reversed form.
* This server is multi-threaded.
*
* @author www.codejava.net
*/
public class ReverseServer {
while (true) {
Socket socket = serverSocket.accept();
System.out.println("New client connected");
new ServerThread(socket).start();
}
import java.io.*;
import java.net.*;
/**
* This thread is responsible to handle client connection.
*
* @author www.codejava.net
*/
public class ServerThread extends Thread {
private Socket socket;
String text;
do {
text = reader.readLine();
String reverseText = new StringBuilder(text).reverse().toString();
writer.println("Server: " + reverseText);
} while (!text.equals("bye"));
socket.close();
} catch (IOException ex) {
System.out.println("Server exception: " + ex.getMessage());
ex.printStackTrace();
}
}
}
As you can see, we just move the processing code to be executed into a separate thread,
implemented in the run() method.
Now let run this new server program and run several client programs, you will see the problem
above has solved. All clients are running smoothly.
In this lab task, you will build a simple client-server system, where you use the client to chat
with a dummy "math" server. The protocol between the client and server is as follows.
Home Task 1
Write a chat room application that allows multiple users to connect to the chat server. Before the
user is able to see the chat window, each user needs to enter his or her name. Once a user is
connected, the server window has to display a connected message with the user’s address. The
user’s chat window should display a welcome message form the server. The chat window should
include a message panel to display chat history. Below the chat panel, there should be a profile
picture, a text field for user to type messages and a send button for sending messages to other
connected users. In addition, there should be a setting button, an image button and an upload
button. Setting button allows the user to change the background colour. Image button allows a user
to select files from the computer. The upload button allows the user to upload the selected image
file to the server. The server saves the image at a file named “image.jpg”. In the user’s chat panel,
each message should display the user’s name followed by the typed message. Please show screen
shot of the server window, and three user chat windows. Three users are “Emily”, “Andrew” and
“Mark.” Please see the result sample in the attached image.
Home Task 2
This exercise converts the above files into a simple email server and email client respectively. The
server conversion has been done for you and is contained in file EmailServer.java, a printed version
of which appears on the following pages for ease of reference. Some of the code for the client has
also been provided for you and is held in file EmailClient.java, a printed version of which is also
provided. You are to complete the coding for the client and then run the server program in one
command window and the client program in each of two further command windows (so that there
are two clients communicating with the server at the same time). The details of this simplified
client-server application are given below.
105
• The server recognises only two users, called 'Dave' and 'Karen'.
• Each of the above users has a message box on the server that can accept a maximum of 10
messages.
• Each user may either send a one-line message to the other or read his/her own messages.
• A count is kept of the number of messages in each mailbox. As another message is received, the
appropriate count is incremented (if the maximum has not been reached). When messages are read,
the appropriate count is reduced to zero.
• When sending a message, the client sends three things: the user's name, the word 'send' and the
message itself.
• When requesting reading of mail, the client sends two things: the user's name and the word 'read'.
• As each message is received by the server, it is added to the appropriate mailbox (if there is
room). If the mailbox is full, the message is ignored.
• When a read request is received, the server first sends an integer indicating the number of
messages (possibly 0) that will be sent and then transmits the messages themselves (after which it
reduces the appropriate message count to 0).
• Each user is to be allowed to 'send' and/or 'read' as many times as he/she wishes, until he/she
decides to quit.
• When the user selects the 'quit' option, the client sends two things: the user's name and the word
'quit'.
Lab 08
Basic Configurations
Objectives
The objective of this lab is that the students get acquainted with basic configurations of
packet tracer tool, computer, and switch.
Activity Outcomes:
106