In another lesson, we learned how to transfer datagrams between computers using the UDP protocol.
In this lesson, we will expand our previous server program to support echo processing of datagrams using the UDP protocol. This will give us a single program that supports three different servers at the same time on different threads.
This program uses sockets to implement three different servers in a single program on an IP network. Two of the servers support the TCP protocol and one supports the UDP protocol.
The program is intended for illustration purposes only. If you use this program for any purpose, you use it at your own risk.
If you use this program, you should tighten the security manager to the degree needed by your situation. You can tighten the security manager by removing the overridden methods that begin with the word check (such as checkAccept) in the class named ServerSecurityManager.
This program implements three different servers.
One of the servers is a UDP "echo" server implemented by a thread monitoring a DatagramSocket on port 7. This server echoes the byte array contained in a datagram received on this port and sends that data back to the client where it originated.
Port 7 is the standard echo port for both UDP and TCP echo servers.
The second server is a TCP "echo" server implemented by a thread monitoring a ServerSocket object on port 7. This server also echoes the data that it receives and sends it back to the client that requested the connection.
The third server is an abbreviated HTTP server implemented by a TCP thread monitoring port 80. Port 80 is the standard HTTP port. This server has the ability to respond to a GET command from a web browser and deliver a file as a stream of bytes.
The different servers were implemented on different ports to illustrate the manner in which threads can be used in Java to service multiple ports. Also, this program illustrates that a single program can provide a mixture of TCP and UDP servers.
A custom security manager is implemented which attempts to prevent the HTTP server from delivering files other than those in the current directory or in a subdirectory of the current directory. Otherwise, the security manager is wide open and doesn't enforce any security at all.
Please DO NOT install this server on your network and leave it unattended because client computers could then connect and have broad access to your computer.
This program was tested using JDK 1.1.3 under Win95.
Both the UDP echo portion of this program and the TCP echo portion can be tested using the program named Sockets08 which was designed specifically for testing the echo portion of the program. A listing of Sockets08 is provided at the end of the lesson.
The HTTP portion of this program can be tested using an ordinary browser with the server name localhost. It can also be tested using the program named Sockets06 which was designed specifically for testing the security manager installed in this program. You saw this program in an earlier lesson. You can also test the security manager using an ordinary browser.
Each of these server objects are thread objects. Thus, the three servers operate concurrently and asynchronously on different threads.
In addition, whenever a server objects needs to service a client, it spawns another thread at a lower priority to service that client. Then the server thread goes back to listening for other clients that may be requesting service.
public class Server02{
public static void main(String[] argv){
System.setSecurityManager(new ServerSecurityManager());
HttpServer httpServerThread = new HttpServer();
EchoServer echoServerThread = new EchoServer();
UdpEchoServer udpEchoServerThread = new UdpEchoServer();
}//end main
}//end class Server02 |
UdpEchoServer(){//constructor
start(); //start the thread and invoke the run() method
}//end constructor |
For brevity, exception handling code has been removed from the discussion, as has some of the code whose only purpose is to display status while the server program is running. That code is available in the program listing that is provided near the end of the lesson.
We begin by instantiating a DatagramSocket object and binding it to port 7.
DatagramSocket datagramSocket = new DatagramSocket(7); |
Note that this program is limited to an incoming data length of 1024 bytes because that is the size of the byte array in the empty DatagramPacket object. If you need it to be larger, simply increase the values in the constructor call for the DatagramPacket object.
The receive() method blocks the thread and waits for a datagram packet to arrive. When a packet does arrive, it is used to populate the empty DatagramPacket object that was passed to the receive() method as a parameter.
Then a new thread of type UdpEchoConnection is instantiated to handle the client request and the newly populated DatagramPacket object is passed as a parameter to the constructor for that thread.
Having disposed of this requirement to service a client, the UdpEchoServer thread goes back to the top of the loop, instantiates another empty DatagramPacket object, and blocks again waiting for the arrival of the next datagram packet.
while(true){
DatagramPacket packet =
new DatagramPacket(new byte[1024],1024);
datagramSocket.receive(packet);
new UdpEchoConnection(packet);
}//end while loop |
This object is saved by the constructor for later use. Then the constructor sets the thread priority to a level below the level of the threads monitoring the ports so that the activity of the UdpEchoConnection thread cannot interfere with the ability of the other threads to properly service those ports.
Once the datagram object is saved and the priority is adjusted, the constructor invokes the start() method which in turn invokes the run() method to get the thread up and running.
class UdpEchoConnection extends Thread{
DatagramPacket packet;
UdpEchoConnection(DatagramPacket packet){//constructor
this.packet = packet;
setPriority( NORM_PRIORITY-1 );
start();//start this thread and invoke the run method
}//end constructor |
The DatagramPacket object that was populated from that packet is directly suitable for sending back to the client as an echo with no modifications required.
However, to illustrate some of the other methods of the DatagramPacket class, I elected to use that object to construct a new object and to send the new object back to the client. This is closer to what might be needed in a server with requirements more complex than simply to echo the incoming packet.
The code for extracting information from one such object for the construction of a new object is shown below. As an exercise, you might consider inserting a date stamp into the data portion of the object so that the packet that is returned is not exactly like the packet that was received but contains a date stamp in addition to the original data.
Note that the following code fragment is simply one long statement.
public void run(){
DatagramPacket packetToSend = new DatagramPacket(
packet.getData(),
packet.getLength(),
packet.getAddress(),
packet.getPort()); |
Then, assuming that no exceptions have been thrown, the socket is closed and the thread terminates normally. If an exception has been thrown, there are several exception handlers provided to deal with the situation. You can view those exception handlers in the listing of the program that follows in the next section.
DatagramSocket datagramSocket = null; datagramSocket = new DatagramSocket(); datagramSocket.send(packetToSend); datagramSocket.close(); |
In addition to the program listing of the server program, this section also contains a listing for a short client program that was designed to test the UDP and TCP echo server portions of this program.
/*File Server02.java Copyright 1998, R.G.Baldwin
This program is an upgrade of the program named Server01
to support a UDP Datagram echo port on port 7.
Also a couple of corrections were made to the earlier
program where a socket wasn't being properly closed
in an exception handler.
This program uses sockets to implement three different
servers in a single program on an IP network. The program
is intended for illustration and experimentation purposes
only.
If you use this program for any purpose, you use it at
your own risk.
If you use this program, you should tighten the security
manager to the degree needed by your situation. You can
tighten the security manager by removing the overridden
methods that begin with the word check (such as
checkAccept) in the class named ServerSecurityManager.
This program implements three different servers.
One of the servers is a UDP "echo" server implemented by
a thread monitoring a DatagramSocket on port 7. This
server echoes the byte array contained in a datagram
received on this port back to the sender of the datagram.
Port 7 is the standard echo port for both UDP and TCP
echo servers.
The second server is a TCP "echo" server implemented by
a thread monitoring a ServerSocket object on port 7.
This server also echoes the string that it receives back
to the the client that requested the connection.
The third server is an abbreviated HTTP server implemented
by a thread monitoring port 80. Port 80 is the standard
HTTP port. This server has the ability to respond to a
GET command from a web browser and serve a file as a stream
of bytes.
The different servers were implemented on different
ports to illustrate the manner in which threads can be
used in Java to service multiple ports. Also, this
program illustrates that a single program can provide
a mixture of TCP and UDP communications.
A custom security manager is implemented which attempts
to prevent the server from serving files other than those
in the current directory or in a subdirectory of the
current directory. Otherwise, the security manager is
wide open and doesn't enforce any security at all.
DO NOT install this server on your network and leave it
unattended because client computers could connect and
have broad access to your computer.
This program was tested using JDK 1.1.3 under Win95.
Both the UDP echo portion of this program and the TCP
echo portion can be tested using the program named
Sockets08 which was designed specifically for testing
the echo portion of this program.
The HTTP portion of this program can be tested using an
ordinary browser with the server name localhost. It can
also be tested using the program named Sockets06 which was
designed specifically for testing the security manager
installed in this program. However, you can also test
the security manager using an ordinary browser.
**********************************************************/
import java.net.*;
import java.io.*;
import java.util.*;
public class Server02{
public static void main(String[] argv){
//Instantiate a new custom security manager.
System.setSecurityManager(new ServerSecurityManager());
//Instantiate a server object to listen on port 80
HttpServer httpServerThread = new HttpServer();
//Instantiate a TCP server object to listen on port 7
EchoServer echoServerThread = new EchoServer();
//Instantiate a UDP server object to listen on port 7
UdpEchoServer udpEchoServerThread = new UdpEchoServer();
}//end main
}//end class Server02
//=======================================================//
//=======================================================//
//This class is used to instantiate a security manager
class ServerSecurityManager extends SecurityManager{
//See the program named Server01 for the security manager
// code that was removed from this section.
//=======================================================//
//=======================================================//
//This class is used to instantiate a UDP server thread
// that listens on port 7 which is the echo port.
class UdpEchoServer extends Thread{
UdpEchoServer(){//constructor
start(); //start the thread and invoke the run() method
}//end constructor
//-----------------------------------------------------//
public void run(){
try{
//Instantiate a DatagramSocket on port 7 (echo port)
DatagramSocket datagramSocket =
new DatagramSocket(7);
System.out.println(
"UDP Echo Server Listening on port 7");
//Loop and listen to port 7. If a call is
// received, spawn a UdpEchoConnection thread to
// deal with it.
while(true){
//This program is limited to echo string lengths
// of 1024 bytes.
DatagramPacket packet =
new DatagramPacket(new byte[1024],1024);
//This statement blocks on the receive() method
// and populates the packet if a call is received.
// The populated packet is passed as a parameter
// to a new thread that is spawned to handle the
// client. Then this thread instantiates a new
// empty packet and goes back to listening.
datagramSocket.receive(packet);
new UdpEchoConnection(packet);
}//end while loop
}catch(IOException e){System.out.println(e);}
}//end run
}//end class UdpEchoServer
//=======================================================//
//This class is used to spawn a thread to deal with a UDP
// call that is received on port 7 which is the echo
// port.
class UdpEchoConnection extends Thread{
DatagramPacket packet;
UdpEchoConnection(DatagramPacket packet){//constructor
System.out.println("Received a call on port 7");
this.packet = packet;
//Operate at a priority that is below the threads
// listening on the ports.
setPriority( NORM_PRIORITY-1 );
start();//start this thread and invoke the run method
}//end constructor
//-----------------------------------------------------//
public void run(){
System.out.println("Running UDP thread for port 7");
//Create a packet to echo based on the data in the
// packet that was received as a parameter.
DatagramPacket packetToSend = new DatagramPacket(
packet.getData(),
packet.getLength(),
packet.getAddress(),
packet.getPort());
//Declare datagram socket outside of try block
DatagramSocket datagramSocket = null;
try{
//Open a new datagram socket
datagramSocket = new DatagramSocket();
//Use the new datagram socket to send the message
// and close the socket.
datagramSocket.send(packetToSend);
datagramSocket.close();
System.out.println("UDP Socket closed");
}//end
catch(UnknownHostException e){
System.out.println(e);
datagramSocket.close();
System.out.println("UDP Socket closed");
}//end catch UnknownHostException
catch(SocketException e){
System.out.println(e);
datagramSocket.close();
System.out.println("UDP Socket closed");
}//end catch SocketException
catch( IOException e){
System.out.println( "I/O error " + e );
datagramSocket.close();
System.out.println("UDP Socket closed");
}//end catch IOException
}//end run method
}//end class UdpEchoConnection
//=======================================================//
//=======================================================//
//This class is used to instantiate a server thread that
// listens on port 7 which is the echo port.
class EchoServer extends Thread{
//See the program named Server01 for the code that was
// removed from this section.
//=======================================================//
//=======================================================//
//This class is used to instantiate a server thread that
// listens on port 80 which is the http port.
class HttpServer extends Thread{
//See the program named Server01 for the code that was
// removed from this section.
//=======================================================// |
/*File Sockets08.java Copyright 1998, R.G.Baldwin
Revised 01/24/98
This program is identical to the program named Sockets08
except that the server name is localhost. This program
was created for the sole purpose of testing the server
program named Server02.
Upgraded from Sockets03 to include UDP echo testing.
This program performs two echo tests with a server
by sending a line of text to the echo port: port 7.
The first echo text is a TCP/IP echo test.
The second test is a UDP Datagram echo test.
You must be logged onto an appropriate network for this
program to run properly. Otherwise, it will throw
an exception of type UnknownHostException.
Most of the program is enclosed in a try/catch block to
deal with possible exceptions.
The program begins by instantiating a String object
containing the name of an echo server that you are
using to test the program.
This is followed by the declaration and initialization of
an int variable identifying the standard echo port number.
The standard echo port is number is 7 for both the TCP and
UDP echo port.
Two String objects are instantiated, one to be used for
the TCP echo test, and the other to be used for the UDP
echo test. They are named msg1 and msg2.
Then the program does all of those things necessary to
conduct the TCP echo test as described in the earlier
program named Sockets03.
After completing the TCP echo test the program closes the
TCP socket and begins the UDP echo test.
The message to be used for the UDP echo test is converted
to a byte array.
An object of type InetAddress is instantiated containing
the address of the server.
A DatagramPacket object is instantiated containing the
byte array along with the address of the server and the
number of the echo port on that server.
A DatagramSocket object is instantiated that will
(hopefully) be used to send the packet to the server.
The send() method is invoked on the socket, passing the
packet as a parameter. This causes the packet to be
sent to the address of the server and port number
previously encapsulated in the packet.
The same DatagramSocket and packet will be used to
receive the packet that is (hopefully) sent back by the
server.
The data in the packet is overwritten with the character x
so that it can later be confirmed that the received data
in the packet is new data and is not simply the residue
of the message originally placed in the packet. The
overwritten data in the package is displayed, consisting
simply of a string of character x.
Then the receive() method is invoked on the DatagramSocket
passing the packet as a parameter. This causes the thread
to block and wait for a packet to be received on the
same port from which the packet was originally sent.
When the physical packet is received from the server,
the data in that physical packet is extracted and written
into the data portion of the packet object that was
provided as a parameter to the receive() method.
The thread is no longer blocked, and program control
moves to a statement that displays the data contained in
the packet object. As expected, the data is an echo of
the message originally sent to the echo port on the
server.
Then the socket is closed and the program terminates.
This program was tested using JDK 1.1.3 under Win95.
Assuming that you connect to a server that supports both
TCP and UDP echo testing, the output from this program
should be:
This is a TCP echo test
xxxxxxxxxxxxxxxxxxxxxxx
This is a UDP echo test
**********************************************************/
import java.net.*;
import java.io.*;
import java.util.*;
class Sockets08{
public static void main(String[] args){
String server = "localhost";
int port = 7; //echo port
String msg1 = "This is a TCP echo test";
String msg2 = "This is a UDP echo test";
//First conduct a TCP echo test
try{
//Get a socket, connected to the specified server
// on the specified port.
Socket socket = new Socket(server,port);
//Get an input stream from the socket
BufferedReader inputStream =
new BufferedReader(new InputStreamReader(
socket.getInputStream()));
//Get an output stream to the socket. Note
// that this stream will autoflush.
PrintWriter outputStream =
new PrintWriter(new OutputStreamWriter(
socket.getOutputStream()),true);
//Send line of text to the server
outputStream.println(msg1);
//Get echoed line back from server and display it
System.out.println(inputStream.readLine());
//Close the TCP socket
socket.close();
//Now conduct a datagram echo test to same port on
// the same server
//Convert the message to a byte array
byte[] udpMsg = msg2.getBytes();
InetAddress addr = InetAddress.getByName(server);
//Create packet to send to the UDP echo port
DatagramPacket packet =
new DatagramPacket(udpMsg,udpMsg.length,addr,port);
//Now get a datagram socket to send the message
DatagramSocket datagramSocket = new DatagramSocket();
//Now send the message
datagramSocket.send(packet);
//Now overwrite the msg in the packet to confirm that
// echo is really received
byte[] dataArray = packet.getData();
for(int cnt = 0; cnt < packet.getLength(); cnt++)
dataArray[cnt] = 'x';
//Display overwritten version
System.out.println(new String(packet.getData()));
//Now receive the echo into the same packet. Echo
// will overwrite current contents of the packet
datagramSocket.receive(packet);
//Display the echo
System.out.println(new String(packet.getData()));
datagramSocket.close();
}//end try
catch(UnknownHostException e){
System.out.println(e);
System.out.println(
"Must be online to run properly.");
}//end catch UnknownHostException
catch(SocketException e){System.out.println(e);}
catch(IOException e){System.out.println(e);}
}//end main
}//end class Sockets08
//=======================================================// |