Lab 7

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

Lab 07

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 familiar with the Socket and ServerSocket classes.

• Be familiar with the Datagram and DatagramPacket classes.

• 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

Activity 1: Connectionless Communication using UDP


The java.net package provides several classes that support socket-based client/server
communication. The InetAddress class encapsulates Internet IP addresses and supports conversion
between dotted decimal addresses and host names. The Socket, ServerSocket, DatagramSocket,
and MulticastSocket classes implement client and server sockets for connection-oriented and
connectionless communication. The DatagramPacket class is used to construct UDP datagram
packets. Two java.net classes define the heart of datagram-based messaging in Java, the
DatagramSocket and the DatagramPacket. The DatagramSocket is the interface through which
DatagramPacket are transmitted. A DatagramPacket is simply an IP-specific wrapper for a block
of data. The DatagramSocket class provides a good interface to the UDP protocol. This class is
responsible for sending and receiving DatagramPacket via the UDP protocol. The most commonly
used DatagramSocket methods are listed below:

•DatagramSocket(). Constructor comes in two formats: one is used to specify the


local port used and the other the system picks an ephemeral local port for you.

• receive(). Receive a DatagramPacket from any remote server.

•send(). Send a DatagramPacket to the remote server specified in the


DatagramPacket.

81
•close(). Tear down local communication resources. After this method has been
called, release this object.

• getLocalPost(). Returns the local port this DatagramSocket is using.


The following program demonstrates how to implement a simple server that converts the text
transmitted by client to uppercase, and sends it back to the client. The client reads line from
standard input (inFromUserstream) , sends to server via socket (outToServer stream). The server
reads line from socket. Server converts line to uppercase, sends back to client. Client reads, prints
modified line from socket (inFromServer stream)
Here’s the code for the server:

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.

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


public class UdpClient {
public static void main(String args[]) throws Exception {
// get server address and open socket
InetAddress serverAdr = InetAddress.getByName(args[0]);
DatagramSocket sock = new DatagramSocket();
// build packet addressed to server, then send it
byte[] outBuf = args[1].getBytes("US-ASCII");
DatagramPacket outPkt = new
DatagramPacket(outBuf,outBuf.length,serverAdr,9876);
sock.send(outPkt);
// create buffer and packet for reply, then receive it
byte[] inBuf = new byte[1000];
DatagramPacket inPkt = new DatagramPacket(inBuf,inBuf.length);
sock.receive(inPkt);
// print buffer contents and close socket
String reply = new String(inBuf,0,inPkt.getLength(),"US-ASCII");
System.out.println(reply);
sock.close();
} }

Activity 2: Socket Programming using TCP


The Socket class implements client connection-based sockets. These sockets are used to develop
applications that utilize services provided by connection-oriented server applications.
The access methods of the Socket class are used to access the I/O streams and connection
parameters associated with a connected socket. The getInetAddress() and getPort() methods get
the IP address of the destination host and the destination host port number to which the socket is
connected. The ServerSocket class implements a TCP server socket. It provides three constructors
that specify the port to which the server socket is to listen for incoming connection requests, an
optional maximum connection request queue length, and an optional Internet
address.
The accept() method is used to cause the server socket to listen and wait until an incoming
connection is established. It returns an object of class Socket once a connection is made. This
Socket object is then used to carry out a service for a single client. The getInetAddress() method

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 {

public static void main(String[] args) {


if (args.length < 1) return;

int port = Integer.parseInt(args[0]);

try (ServerSocket serverSocket = new ServerSocket(port)) {

System.out.println("Server is listening on port " + port);

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());
}

} catch (IOException ex) {


System.out.println("Server exception: " + ex.getMessage());
ex.printStackTrace();
}
}
84
}

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 {

public static void main(String[] args) {


if (args.length < 2) return;

String hostname = args[0];


int port = Integer.parseInt(args[1]);

try (Socket socket = new Socket(hostname, port)) {

InputStream input = socket.getInputStream();


BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String time = reader.readLine();
System.out.println(time);

} catch (UnknownHostException ex) {


System.out.println("Server not found: " + ex.getMessage());
} catch (IOException ex) {
System.out.println("I/O error: " + ex.getMessage());
}
}
}

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:

After reading this chapter, you should:


• understand what is meant by a thread (in a programming context);
• appreciate the need for multithreaded programming;
• be aware of typical circumstances under which multithreading might be appropriate;
• know how to implement threads in Java;
• know how to implement variable locking in Java;
• be aware of the danger posed by deadlock;
• know what Java methods to use in order to improve thread efficiency and reduce the likelihood
of deadlock;
• know how to implement a multithreaded server;
• know how to implement a non-blocking server via multiplexing.

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.

Using Threads in Java

Java is unique amongst popular programming languages in making multithreading directly


accessible to the programmer, without him/her having to go through an operating system API.
Unfortunately, writing multithreaded programs can be rather tricky and there are certain pitfalls
that need to be avoided. These pitfalls are caused principally by the need to coordinate the activities
of the various threads, as will be seen in Section 3.4.
In Java, an object can be run as a thread if it implements the inbuilt interface Runnable, which has
just one method: run. Thus, in order to implement the interface, we simply have to provide a
definition for method run. Since the inbuilt class Thread implements this interface, there are two
fundamental methods for creating a thread class:
• create a class that extends Thread;
• create a class that does not extend Thread and specify explicitly
that it implements Runnable.

Extending the Thread Class


The run method specifies the actions that a thread is to execute and serves the same purpose for
the process running on the thread as method main does for a full application program. Like main,
run may not be called directly. The containing program calls the start method (inherited from class
Thread), which then automatically calls run.
Class Thread has seven constructors, the two most common of which are:
• Thread()
87
• Thread(String<name>)

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

Thread firstThread = new Thread();


Thread secondThread = new Thread("namedThread");
System.out.println(firstThread.getName()); System.out.println(secondThread.getName());

The output from the above lines would be:


Thread-0 namedThread

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:

myThread.sleep(1500); //Pause for 1.5 seconds.

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

public class ThreadShowName extends Thread


{
public static void main (String[] args)
{
ThreadShowName thread1, thread2; thread1 = new ThreadShowName();
thread2 = new ThreadShowName();
thread1.start(); //Will call run.
thread2.start(); //Will call run.
}

public void run()


{
int pause; for (int i=0; i<10; i++)
{
try
{
System.out.println( getName()+" being executed.");
pause = (int)(Math.random()*3000);
sleep(pause);
//0-3 seconds.
}
catch (InterruptedException interruptEx)
89
{
System.out.println(interruptEx); } }
}
}

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.

public class ThreadHelloCount


{

90
public static void main(String[] args)
{
HelloThread hello = new HelloThread();
CountThread count = new CountThread();
hello.start();
count.start();
}
}

class HelloThread extends Thread


{
public void run()
{
int pause; for (int i=0; i<5; i++)
{
try
{
System.out.println("Hello!");
//Again, introduce an element //of randomness…
pause = (int)(Math.random()*3000);
sleep(pause);
}
catch (InterruptedException interruptEx)
{
System.out.println(interruptEx);
}
}
}
}

class CountThread extends Thread


{
int pause;
public void run()

91
{
for (int i=0; i<5; i++)
{
try
{
System.out.println(i); pause=(int)(Math.random()*3000); sleep (pause);

catch (InterruptedException interruptEx)


{
System.out.println(interruptEx);
}
}
}
}

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.

The basic technique involves a two-stage process:


1. the main thread (the one running automatically in method main) allocates individual threads to
incoming clients;
2. the thread allocated to each individual client then handles all subsequent interaction between
that client and the server (via the thread's run method).

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.*;

public class MultiEchoServer {


private static ServerSocket serverSocket; private static final int PORT = 1234;
public static void main(String[] args)
throws IOException
{
try
{
serverSocket = new ServerSocket(PORT);
}
catch (IOException ioEx)
{
System.out.println("\nUnable to set up port!");
System.exit(1);

}
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);
}
}

class ClientHandler extends Thread


{
private Socket client;
private Scanner input;
private PrintWriter output;
public ClientHandler(Socket socket)
{
//Set up reference to associated socket...
client = socket;
try
{
input = new Scanner(client.getInputStream());
output = new PrintWriter( client.getOutputStream(),true);
}
catch(IOException ioEx)
{ ioEx.printStackTrace();
}
}
public void run()
{
String received;
do
{

//Accept message from client on //the socket's input stream...


received = input.nextLine();

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...

Scanner userEntry = new Scanner(System.in);


String message, response;
do
{
System.out.print( "Enter message ('QUIT' to exit): ");
message = userEntry.nextLine();
//Send message to server on the //socket's output stream...
//Accept response from server on the //socket's intput stream...
networkOutput.println(message);
response = networkInput.nextLine();
//Display server's response to user...

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 {

public static void main(String[] args) {


if (args.length < 1) return;

int port = Integer.parseInt(args[0]);

try (ServerSocket serverSocket = new ServerSocket(port)) {

System.out.println("Server is listening on port " + port);

while (true) {
Socket socket = serverSocket.accept();
System.out.println("New client connected");

InputStream input = socket.getInputStream();


BufferedReader reader = new BufferedReader(new InputStreamReader(input));

OutputStream output = socket.getOutputStream();


PrintWriter writer = new PrintWriter(output, true);
String text;

do {
text = reader.readLine();
99
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();
}
}
}
you can see, the server continues serving the client until it says ‘bye’. Run this server program
using the following command:

java ReverseServer 9090

The server is up and running, waiting for incoming requests from clients:

Server is listening on port 9090

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 {

public static void main(String[] args) {


if (args.length < 2) return;

100
String hostname = args[0];
int port = Integer.parseInt(args[1]);
try (Socket socket = new Socket(hostname, port)) {

OutputStream output = socket.getOutputStream();


PrintWriter writer = new PrintWriter(output, true);
Console console = System.console();
String text;

do {
text = console.readLine("Enter text: ");

writer.println(text);

InputStream input = socket.getInputStream();


BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String time = reader.readLine();

System.out.println(time);

} while (!text.equals("bye"));

socket.close();

} catch (UnknownHostException ex) {

System.out.println("Server not found: " + ex.getMessage());

} catch (IOException ex) {

System.out.println("I/O error: " + ex.getMessage());


}
}
}
As you can see, this client program is running until the user types ‘bye’. Run it using the
following command:
java ReverseClient localhost 9090
101
Then it asks you to enter some text:
Enter text:_

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.

Let’s see how to solve this problem in the next example.

Activity 8: TCP-based multi-threaded client/server application where the server is able to


handle multiple clients

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 {

public static void main(String[] args) {


if (args.length < 1) return;

int port = Integer.parseInt(args[0]);


102
try (ServerSocket serverSocket = new ServerSocket(port)) {

System.out.println("Server is listening on port " + port);

while (true) {
Socket socket = serverSocket.accept();
System.out.println("New client connected");

new ServerThread(socket).start();
}

} catch (IOException ex) {


System.out.println("Server exception: " + ex.getMessage());
ex.printStackTrace();
}
}
}

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;

public ServerThread(Socket socket) {


this.socket = socket;
}

public void run() {


try {
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));

OutputStream output = socket.getOutputStream();


103
PrintWriter writer = new PrintWriter(output, true);

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.

1. Graded Lab Tasks


Note: The instructor can design graded lab activities according to the level of difficult and
complexity of the solved lab activities. The lab tasks assigned by the instructor should be evaluated
in the same lab.

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.

 The server is first started on a known port.


 The client program is started (server IP and port is provided on the command line).
104
 The client connects to the server, and then asks the user for input. The user enters a
simple arithmetic expression string (e.g., "1 + 2", "5 - 6", "3 * 4"). The user's input is sent
to the server via the connected socket.
 The server reads the user's input from the client socket, evaluates the expression, and
sends the result back to the client.
 The client should display the server's reply to the user, and prompt the user for the next
input, until the user terminates the client program with Ctrl+C.

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

You might also like