In this project you will implement a server for the Purdue Safe Walk Program. The server you are going to write will handle requests and commands sent from the user.
Safe Walk is a campus safety system (currently done manually) that allows people on campus to request that someone walk with them from one location to another on campus. For more details, refer to this document.
Here are some definitions and terms that will be used throughout this project.
A location is a building in Purdue campus. A location is identified by a unique acronym. First, for simplicity, the following locations will be defined in the system:
CL50
: Class of 1950 Lecture HallEE
: Electrical Engineering BuildingLWSN
: Lawson Computer Science BuildingPMU
: Purdue Memorial UnionPUSH
: Purdue University Student Health Center*
: Any one of the location aboveYou may think of *
as a wildcard that matches with any of the first five locations.
As you will see later, a user message will contain two locations, FROM
and TO
. Locations not on the list above will be considered invalid.
A user is anyone who sends content to the server.
A requester is a user who asks for help (instead of sending commands). Again, as you will find later, not all users are requesters.
telnet
and netcat (nc
).Now you are going to write your own server program.
Name your program SafeWalkServer
. To run it, use the command java SafeWalkServer port
where the port
argument should be an integer between 1025
and 65535
inclusive. If the port
argument is given but invalid, print an error message (any meaningful message) and exit gracefully. If the port
argument is not provided, your program should use a port that is automatically allocated, in which case you must print this port number to stdout before the server starts accept
ing requests. For example,
Port not specified. Using free port 8888.
where 8888
is the port allocated automatically. Note that 8888 is just an example.
If the port is already used (the constructor of ServerSocket will throw an exception). Print an error message and exit gracefully.
Your SafeWalkServer
should have two constructors corresponding to the situations where port may or may not be given.
Finish these two methods:
/** * Construct the server, and create a server socket, * bound to the specified port. * * @throws IOException IO error when opening the socket. */ public SafeWalkServer(int port) throws IOException { //TODO: finish the method } /** * Construct the server, and create a server socket, * bound to a port that is automatically allocated. * * @throws IOException IO error when opening the socket. */ public SafeWalkServer() throws IOException { //TODO: finish the method }
Note that the constructors may throw exceptions if any problem occurs during the execution of the constructor. If the object is successfully instantiated without exceptions then the server object is ready to run.
To enable the socket to be reclaimed by the operating system when the program exits, be sure to set the address to reusable by calling setReuseAddress(true)
. For more details, refer to setReuseAddress.
Your SafeWalkServer
class must extend ServerSocket
class and implement the Runnable
interface. Thus, you must provide a run
method. You must also provide a method isPortValid
. The following javadoc comments describe their functionality.
/** * Start a loop to accept incoming connections. */ public void run() { //TODO: finish this method } /** * Return true if the port entered by the user is valid. Else return false. * Return false if you get a NumberFormatException while parsing the parameter port * Call this method from main() before creating SafeWalkServer object * Note that you do not have to check for validity of automatically assigned port */ public static boolean isPortValid(String port) { //TODO: finish this method }
Note that your server should not start accept
ing incoming requests before run()
is called.
To make the server run forever, your run()
method may look something like:
while (/* condition */) { Socket client = accept(); // i.e., do something with client }
Note that accept()
blocks the execution of the server until there is an incoming connection.
It is very difficult for two people to communicate if one does not understand the other’s language. Similarly, a server cannot talk to clients unless they conform to the same protocol. The protocol that your server uses will be simple text messages.
There are two types of messages that your server needs to handle: request messages and commands.
The request message is simple text in CSV (comma-separated value) form, with three fields: NAME,FROM,TO
where
NAME
is the human-friendly name of the user. You can assume it does not contain any commas.FROM
is the location the user wants to move from (FROM
cannot be *
).TO
is the destination of the user’s movement (TO
cannot be the same as FROM
).For example, a message Tom Riddle,LWSN,PUSH
means the person named Tom Riddle
wants to move from LWSN
to PUSH
, while the message Harry Potter,LWSN,*
means that Harry Potter
wants to move from LWSN
to *
location (perhaps to help someone as a volunteer).
There are three commands that your server needs to handle. Note that they all start with a colon (:
).
:RESET
ERROR: connection reset
and close its socket, before discarding the request.RESPONSE: success
and close its socket.:SHUTDOWN
:RESET
does, close the socket and whatever streams your server uses, and exit the run loop).:PENDING_REQUESTS,TASK,FROM,TO
TASK
refers to the information that the user is seeking. Valid values for TASK
are #
and *
. FROM
and TO
are one of the locations listed above.TASK
, FROM
and TO
. Close the client socket after the write operation. While writing unpaired request messages, use the 3-tuple form (see example below):
) but does not conform to the above specifications then respond to the client with ERROR: invalid command
and close its socket.TASK | FROM | TO | Write to the client socket (Action) |
---|---|---|---|
# |
* |
* |
total number of pending requests |
# |
Any valid location other than * |
* |
total number of pending requests starting at FROM |
# |
* |
Any valid location other than * |
total number of pending requests destined to TO |
* |
* |
* |
list all the pending requests |
Example commands and their corresponding response messages are as shown below. Note that these are examples, you should not hard code values in your program. You should stick to the format of the response messages shown below.
:PENDING_REQUESTS,#,LWSN,* // command RESPONSE: # of pending requests from LWSN = 1 // response :PENDING_REQUESTS,#,*,PUSH // command RESPONSE: # of pending requests to PUSH = 2 // response :PENDING_REQUESTS,#,*,* // command RESPONSE: # of pending requests = 2 // response :PENDING_REQUESTS,*,*,* // command [[Alicia, PMU, PUSH], [Riya, LWSN, EE]] // response
Whenever accept()
returns a Socket
object, your server has received a new request/command to handle. Your server should handle a request/command by doing the following steps: validity check, handle request/command, clean up.
Before performing a request/command, your server should first check if it is valid. The rules are as follows:
:
), make sure it is in the list of defined commands.NAME
, FROM
, TO
), separated by commas; and thatFROM
and TO
are known locations;FROM
cannot be *
;TO
must be different from FROM
.If the request is invalid, respond with ERROR: invalid request
to the client and close its socket. If the command is invalid, respond with ERROR: invalid command
to the client and close its socket.
If the message is a valid command, perform it as specified in the list of commands.
If the message is a valid request, try to find a match for it:
FROM
location of paired clients must be the same;TO
location of paired clients must be the same, or, one and only one can have its TO
be *
.If there is no suitable pair for the requester, put it on hold:
close
the client socket when storing them because you will need the connection later.If a matched pair is found, move the on-hold client out of the list, respond to each of the pair their match’s request message prefixed with RESPONSE:
, and close their sockets.
For example, given
Tom Riddle,LWSN,PUSH Vegeta,LWSN,EE Harry Potter,LWSN,* Andy,CL50,* Bella,CL50,* Cyndi,EE,* Woody,CL50,PMU Goku,LWSN,EE Edward,CL50,PUSH John Doe,PUSH,PMU :PENDING_REQUESTS,*,*,*
the server will do the following:
LWSN
to PUSH
RESPONSE: Tom Riddle,LWSN,PUSH
RESPONSE: Harry Potter,LWSN,*
RESPONSE: Andy,CL50,*
RESPONSE: Woody,CL50,PMU
RESPONSE: Goku,LWSN,EE
RESPONSE: Vegeta,LWSN,EE
The client who sends :PENDING_REQUESTS,*,*,*
will get
[[Cyndi, EE, *], [John Doe, PUSH, PMU]]
If the server then receives command :RESET
then Cyndi
and John Doe
will get the response ERROR: connection reset
and have their connections closed.
Use the readLine
method of BufferedReader
when reading from input streams, and the println
method of PrintWriter
when writing to output streams. Refer to textbook or lecture slides from week 10 (External Communication) for more details.
After a client has been served (paired with another client, or connection reset), close its streams and socket gracefully so that Java garbage collector knows to reclaim the resources.
For testing the server you can either write JUnit tests or use networking tools. We strongly suggest that you write JUnit tests as it can automate the testing process.
Client.java
. Use this class to start a client from your test method.Client
class.If you are clueless at this point then refer to
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.SocketTimeoutException; /** * Client class for SafeWalkServer */ public class Client implements Runnable { private final String host; private final int port; private final String message; private boolean timeout; private String result; /** * Used to create Client socket without timeout */ public Client(String host, int port, String msg) { this.host = host; this.port = port; this.message = msg; } /** * Necessary for JUnit testing (and otherwise) * Used to create Client socket with timeout */ public Client(String host, int port, String msg, boolean value) { this.host = host; this.port = port; this.message = msg; timeout = value; } public String getResult() { return result; } public void run() { // try with resource block is used here try (Socket s = new Socket(host, port); PrintWriter out = new PrintWriter(s.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader (s.getInputStream()));) { if (timeout) s.setSoTimeout(1000); out.println(message); result = in.readLine(); } catch (SocketTimeoutException e) { /* During testing the following message might get printed. * It does not always mean that there is an error in the program */ System.out.println("Connection timed out"); } catch (IOException e) { System.err.println(e); } } public static void main(String[] args) { Client c = new Client(args[0], Integer.parseInt(args[1]), args[2]); Thread t = new Thread(c); t.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(c.getResult()); } }
Using the lab computers, or a personal computer running Linux or Mac OS X, you will have access to two simple UNIX networking tools that may help you in the initial testing, and are described below.
One is telnet
. To use telnet
to send messages to your server, issue the following command in your terminal:
$ telnet HOST_NAME PORT_NUMBER
where HOST_NAME
and PORT_NUMBER
are the host and port on which your server program is running. Here, you can use 127.0.0.1
or localhost
as the host name if you wish to connect to a port on the local machine.
If the connection is established, you can send text by typing it.
The other is netcat
, which prints the content in its stdin
to a remote socket, and prints the response to stdout
. An example command may be
$ nc data.cs.purdue.edu 14180 < client_msg.txt
which assumes your server runs on data.cs.purdue.edu:14180
and the request message is stored in client_msg.txt
.
If you are developing on a Windows computer, you may or may not have access to the telnet
command by default. You can test this by opening the command prompt and trying to run it. If you don’t have access, an alternative is to use ncat
, an implementation of netcat
for Windows. Download from here. Use it with the form
$ ncat <host> <port>
and then type your request or command and hit enter.
/**
* Project 5
* @author teamMemberOne, login id, labsec
* @author teamMemberTwo, login id, labsec (can be omitted if working alone)
*/
isPortValid()
method and the Shutdown and Reset command handling. You may implement the other features as well, but we will not consider them while grading the intermediate submission. SafeWalkServer.java
for grading using the turn-in command $ turnin -v -c cs180 -p project5 SafeWalkServer.java
SafeWalkServer.java
for grading using the turn-in command $ turnin -v -c cs180 -p project5Final SafeWalkServer.java
NOTE:
:RESET
command, then both Alice and Bob have to receive proper response messages.The grading rubric is as follows:
isPortValid()
You have to be 100% sure of the quality of your product to give a money-back guarantee. This describes us perfectly. Make sure that this guarantee is totally transparent.
Read moreEach paper is composed from scratch, according to your instructions. It is then checked by our plagiarism-detection software. There is no gap where plagiarism could squeeze in.
Read moreThanks to our free revisions, there is no way for you to be unsatisfied. We will work on your paper until you are completely happy with the result.
Read moreYour email is safe, as we store it according to international data protection rules. Your bank details are secure, as we use only reliable payment systems.
Read moreBy sending us your money, you buy the service we provide. Check out our terms and conditions if you prefer business talks to be laid out in official language.
Read more