For this activity you will convert a simple single-threaded server to a multi-threaded server. You will create 2 versions, one that allows threads to grow unbounded, and one that sets the number of allowed clients at a time to a fixed number. This part is to practice threads on a smaller scale than activity 2.
You are given starting code of a single-threaded server. There is a Client provided, which you should use for your implementation. It also specifies a protocol which you need to implement as is.
You are given starter code for this assignment:
You can have any package structure you want and add new classes etc. Important, you should only have 1 Readme and 1 build.gradle file for all 3 tasks. Each task (server) can be started separately and work only with the features described. You can make any changes in the given files you like, BUT the protocol should work as described in the code.
Presently, all the Performer does is add strings to an array. Make it more interesting by implementing the following protocol:
You can add new classes here of course. Task 1 should still run as is!
You can add new classes here of course. Task 1 and 2 should still run as is.
Client.java
package taskone;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Base64;
import java.util.Scanner;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.json.JSONObject;
/**
* Class: Client
* Description: Client tasks.
*/
public class Client {
private static BufferedReader stdin;
/**
* Function JSONObject add().
*/
public static JSONObject add() {
String strToSend = null;
JSONObject request = new JSONObject();
request.put("selected", 1);
try {
System.out.print("Please input the string: ");
strToSend = stdin.readLine();
} catch (IOException e) {
e.printStackTrace();
}
request.put("data", strToSend);
return request;
}
/**
* Function JSONObject remove().
*/
public static JSONObject pop() {
String strToSend = null;
int inNum;
JSONObject request = new JSONObject();
request.put("selected", 2);
return request;
}
/**
* Function JSONObject display().
*/
public static JSONObject display() {
JSONObject request = new JSONObject();
request.put("selected", 3);
request.put("data", "");
return request;
}
/**
* Function JSONObject count().
*/
public static JSONObject count() {
JSONObject request = new JSONObject();
request.put("selected", 4);
request.put("data", "");
return request;
}
/**
* Function JSONObject reverse().
*/
public static JSONObject switching() {
String strToSend = null;
int inNum;
JSONObject request = new JSONObject();
request.put("selected", 5);
int numInput = 0;
int first = 0;
int last = 0;
while (true) {
System.out.print("Please input the index: ");
try {
strToSend = stdin.readLine();
} catch (IOException e) {
e.printStackTrace();
}
try {
inNum = Integer.parseInt(strToSend);
if (numInput == 0) {
first = inNum;
numInput = 1;
} else {
last = inNum;
break;
}
} catch (NumberFormatException ne) {
System.out.println("Input is not number, continue");
}
}
request.put("data", first+ " " + last);
return request;
}
/**
* Function JSONObject quit().
*/
public static JSONObject quit() {
JSONObject request = new JSONObject();
request.put("selected", 0);
request.put("data", ".");
return request;
}
/**
* Function main().
*/
public static void main(String[] args) throws IOException {
String host;
int port;
Socket sock;
stdin = new BufferedReader(new InputStreamReader(System.in));
try {
if (args.length != 2) {
// gradle runClient -Phost=localhost -Pport=9099 -q --console=plain
System.out.println("Usage: gradle runClient -Phost=localhost -Pport=9099");
System.exit(0);
}
host = args[0];
port = -1;
try {
port = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
System.out.println("[Port] must be an integer");
System.exit(2);
}
sock = new Socket(host, port);
OutputStream out = sock.getOutputStream();
InputStream in = sock.getInputStream();
Scanner input = new Scanner(System.in);
int choice;
do {
System.out.println();
// TODO: you will need to change the menu based on the tasks for this assignment, see Readme!
System.out.println("Client Menu");
System.out.println("Please select a valid option (1-5). 0 to diconnect the client");
System.out.println("1. add < string > - adds a string to the list and display it");
System.out.println("2. pop - remove the top elemnt");
System.out.println("3. display - display the list");
System.out.println("4. count - returns the elements in the list");
System.out.println("5. switch < int > < int > - switch two string");
System.out.println("0. quit");
System.out.println();
choice = input.nextInt(); // what if not int.. should error handle this
JSONObject request = null;
switch (choice) {
case (1):
request = add();
break;
case (2):
request = pop();
break;
case (3):
request = display();
break;
case (4):
request = count();
break;
case (5):
request = switching();
break;
case (0):
request = quit();
break;
default:
System.out.println("Please select a valid option (0-6).");
break;
}
if (request != null) {
System.out.println(request);
NetworkUtils.send(out, JsonUtils.toByteArray(request));
byte[] responseBytes = NetworkUtils.receive(in);
JSONObject response = JsonUtils.fromByteArray(responseBytes);
if (response.has("error")) {
System.out.println(response.getString("error"));
} else {
System.out.println();
System.out.println("The response from the server: ");
System.out.println("datatype: " + response.getString("type"));
System.out.println("data: " + response.getString("data"));
System.out.println();
String typeStr = (String) response.getString("type");
if (typeStr.equals("quit")) {
sock.close();
out.close();
in.close();
System.exit(0);
}
}
}
} while (true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
JsonUtils.java
package taskone;
import org.json.JSONObject;
/**
* Class: JsonUtils
* Description: Json Utilities.
*/
public class JsonUtils {
public static JSONObject fromByteArray(byte[] bytes) {
String jsonString = new String(bytes);
System.out.println(jsonString);
return new JSONObject(jsonString);
}
public static byte[] toByteArray(JSONObject object) {
return object.toString().getBytes();
}
}
NetworkUtils.java
package taskone;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Class: NetworkUtils
* Description: NetworkUtils for send/read/receive.
*/
public class NetworkUtils {
// https://mkyong.com/java/java-convert-byte-to-int-and-vice-versa/
public static byte[] intToBytes(final int data) {
return new byte[] { (byte) ((data >> 24) & 0xff), (byte) ((data >> 16) & 0xff),
(byte) ((data >> 8) & 0xff), (byte) ((data >> 0) & 0xff), };
}
// https://mkyong.com/java/java-convert-byte-to-int-and-vice-versa/
public static int bytesToInt(byte[] bytes) {
return ((bytes[0] & 0xFF) << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8)
| ((bytes[3] & 0xFF) << 0);
}
/**
* send the bytes on the stream.
*/
public static void send(OutputStream out, byte... bytes) throws IOException {
out.write(intToBytes(bytes.length));
out.write(bytes);
out.flush();
}
// read the bytes on the stream
private static byte[] read(InputStream in, int length) throws IOException {
byte[] bytes = new byte[length];
int bytesRead = 0;
try {
bytesRead = in.read(bytes, 0, length);
} catch (IOException e1) {
e1.printStackTrace();
}
if (bytesRead != length) {
return null;
}
return bytes;
}
// first 4 bytes we read give us the length of the message we are about to
// receive
// next we call read again with the length of the actual bytes in the data we
// are interested in
/**
* Receive the bytes on the stream.
*/
public static byte[] receive(InputStream in) throws IOException {
byte[] lengthBytes = read(in, 4);
if (lengthBytes == null) {
return new byte[0];
}
int length = NetworkUtils.bytesToInt(lengthBytes);
byte[] message = read(in, length);
if (message == null) {
return new byte[0];
}
return message;
}
}
Performer.java
package taskone;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
/**
* Class: Performer
* Description: Threaded Performer for server tasks.
*/
class Performer {
private StringList state;
private Socket conn;
public Performer(Socket sock, StringList strings) {
this.conn = sock;
this.state = strings;
}
public JSONObject add(String str) {
JSONObject json = new JSONObject();
json.put("datatype", 1);
json.put("type", "add");
state.add(str);
json.put("data", state.toString());
return json;
}
public static JSONObject error(String err) {
JSONObject json = new JSONObject();
json.put("error", err);
return json;
}
public void doPerform() {
boolean quit = false;
OutputStream out = null;
InputStream in = null;
try {
out = conn.getOutputStream();
in = conn.getInputStream();
System.out.println("Server connected to client:");
while (!quit) {
byte[] messageBytes = NetworkUtils.receive(in);
JSONObject message = JsonUtils.fromByteArray(messageBytes);
JSONObject returnMessage = new JSONObject();
int choice = message.getInt("selected");
switch (choice) {
case (1):
String inStr = (String) message.get("data");
returnMessage = add(inStr);
break;
default:
returnMessage = error("Invalid selection: " + choice
+ " is not an option");
break;
}
// we are converting the JSON object we have to a byte[]
byte[] output = JsonUtils.toByteArray(returnMessage);
NetworkUtils.send(out, output);
}
// close the resource
System.out.println("close the resources of client ");
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Server.java
package taskone;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import org.json.JSONObject;
/**
* Class: Server
* Description: Server tasks.
*/
class Server {
public static void main(String[] args) throws Exception {
int port;
StringList strings = new StringList();
if (args.length != 1) {
// gradle runServer -Pport=9099 -q --console=plain
System.out.println("Usage: gradle runServer -Pport=9099 -q --console=plain");
System.exit(1);
}
port = -1;
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
System.out.println("[Port] must be an integer");
System.exit(2);
}
ServerSocket server = new ServerSocket(port);
System.out.println("Server Started...");
while (true) {
System.out.println("Accepting a Request...");
Socket sock = server.accept();
Performer performer = new Performer(sock, strings);
performer.doPerform();
try {
System.out.println("close socket of client ");
sock.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
StringList.java
package taskone;
import java.util.List;
import java.util.ArrayList;
class StringList {
Liststrings = new ArrayList ();
public void add(String str) {
int pos = strings.indexOf(str);
if (pos < 0) {
strings.add(str);
}
}
public boolean contains(String str) {
return strings.indexOf(str) >= 0;
}
public int size() {
return strings.size();
}
public String toString() {
return strings.toString();
}
}