Initial commit

This commit is contained in:
David Westgate 2021-01-25 13:57:29 -08:00
commit bf7100b28d
4 changed files with 335 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*class
*log

28
Main.java Normal file
View File

@ -0,0 +1,28 @@
/*
Main class for server execution.
Arguments: None
*/
public class Main {
private static Server server;
public static void main(String[] args) {
server = new Server(4000);
System.out.println("Starting up server ....");
server.start();
while (server.getActive() == true) {
//Loop while server is running
}
System.out.println("Server terminating. Goodbye");
}
}

181
Server.java Normal file
View File

@ -0,0 +1,181 @@
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.*;
/*
Server to create socket and manage threads of client connections.
*/
public class Server {
private final boolean DEBUG = false;
private final int NUMSOCKETS = 5; //Number of socket threads to run
private final int REFRATE = 10; //Refresh period of report, in seconds
private final String fileName = "numbers.log";
private ServerSocket serverSocket;
private SocketThread[] threads;
private ThreadGroup threadGroup;
private int port;
private boolean active;
private long refTime;
private ArrayList<String> uniqueList; //A final list of all the unique 9 digit numbers
private List<String> incomingList;//A list to hold all new numbers from socket threads
private int totalUniqueCount;
private int newUniqueCount;
private int newDupesCount;
private BufferedWriter writer;
//initialize variables, open the socket.
public Server(int port) {
try {
writer = new BufferedWriter(new FileWriter(fileName));
} catch (IOException e) {
e.printStackTrace();
}
totalUniqueCount = 0;
newUniqueCount = 0;
newDupesCount = 0;
refTime = System.nanoTime();
this.port = port;
active = true;
threads = new SocketThread[5];
threadGroup = new ThreadGroup("socket threads");
uniqueList = new ArrayList<String>();
incomingList = Collections.synchronizedList(new LinkedList<String>()); //thread safe list
}
//Start the server, called by main
public void start() {
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}
//start all socket threads
for (int i = 0; i < NUMSOCKETS; i++) {
threads[i] = new SocketThread(threadGroup, serverSocket, incomingList);
threads[i].start();
}
//Loop forever while all 5 threads running.
while (threadGroup.activeCount() == NUMSOCKETS) {
//Update unique list and print report every refresh period
if ((System.nanoTime() - refTime) / 1000000000 > REFRATE) {
updateList();
displayReport();
refTime = System.nanoTime();
}
}
stop(); // a thread stops running, implies terminate. Stop the server
updateList(); //Last update after stopping all connections.
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//Method called to stop all threads and shut down the server
public void stop() {
//First, Loop through all threads and tell them to release clients and self-terminate
for (int i = 0; i < NUMSOCKETS; i++) {
if ((threads[i] != null) && (threads[i].isAlive())) {
threads[i].setTerminate(true);
threads[i].setRelease(true);
}
}
//Second, Wait for each thread to die
for (int i = 0; i < NUMSOCKETS; i++) {
try {
//Close the server socket
serverSocket.close();
//If the thread is still running now, wait for it to die
if (threads[i] != null && threads[i].isAlive()) {
threads[i].join();
}
} catch (Exception e) {
e.printStackTrace();
}
}
threadGroup.destroy(); //If all threads dead, group will destroy without error
active = false;
}
//Update list of unique new numbers from the list of incoming numbers
private void updateList() {
int index = uniqueList.size();
int newSize = 0;
newUniqueCount = 0;
newDupesCount = 0;
//Cannot allow other threads to modify incomingList while we are working with it.
synchronized (incomingList) {
uniqueList.addAll(incomingList);
incomingList.clear();
}
//Calculating report data
newDupesCount = removeDupes(uniqueList);
newSize = uniqueList.size();
newUniqueCount = newSize - index;
totalUniqueCount = newSize;
for (int i = index; i < uniqueList.size(); ++i) {
updateFile(uniqueList.get(i));
}
}
//Print report to console
private void displayReport() {
// System.out.print("\033[H\033[2J"); //clear the console (optional)
System.out.println("Received " + newUniqueCount + " unique numbers, "
+ newDupesCount + " duplicates. Unique total: " + totalUniqueCount);
}
//Write the parameter string to the log file
private void updateFile(String newNum) {
try {
writer.write(newNum);
writer.newLine();
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//Allow main program to check if server is still active.
public boolean getActive() {
return this.active;
}
//Remove duplicate entries from src list and return number of duplicates
private int removeDupes(ArrayList<String> src) {
if (src == null || src.size() < 2) {
return 0;
}
int size = src.size();
LinkedHashSet<String> noDupes = new LinkedHashSet<String>(src);
src.clear();
src.addAll(noDupes);
return size - src.size();
}
}

124
SocketThread.java Normal file
View File

@ -0,0 +1,124 @@
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.LinkedList;
import java.util.List;
/*
The server creates SocketThread objects to accept new sockets.
SocketThread must manage socket connection and properly process data.
*/
public class SocketThread extends Thread {
private final String expression = "^[0-9]{9}$";//regex for 9 digit number
private final boolean DEBUG = false;
private Socket clientSocket;
private ServerSocket serverSocket;
private ThreadGroup threadGroup;
private InputStream inputStream;
private OutputStream outputStream;
private BufferedReader bufferedReader;
private PrintWriter printWriter;
private List<String> incomingList;
private String line; //Buffer string
private boolean release; //flag to release current client
private boolean terminate; //flag to terminate this thread
public SocketThread(ThreadGroup threadGroup, ServerSocket serverSocket, List<String> incomingList) {
super(threadGroup, "");
this.incomingList = incomingList;
this.serverSocket = serverSocket;
this.threadGroup = threadGroup;
line = null;
release = false;
terminate = false;
}
//Methods allows the server to tell thread to finish up
public void setRelease(boolean release) {
this.release = release;
}
public void setTerminate(boolean terminate) {
this.terminate = terminate;
}
public void run() {
try {
//Loop to re-open socket and listen after it is closed
while ((clientSocket == null) && !terminate) {
release = false;
clientSocket = serverSocket.accept();
inputStream = clientSocket.getInputStream();
outputStream = clientSocket.getOutputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
printWriter = new PrintWriter(outputStream, true);
//Loop to keep reading lines from buffer while socket is connected
while (!release && !terminate) {
line = bufferedReader.readLine();
//If the line read is 9 digits
if (line != null && (line.length() == 9)) {
//Case of valid, 9 digit number
if (line.matches(expression)) {
//Only allow one thread to add to shared list at a time
synchronized (incomingList){
incomingList.add(line);
}
}
//Case of terminate input
else if (line.equals("terminate")) {
release = true;
terminate = true;
}
//Case of invalid input
else {
release = true;
}
}
//Case of invalid input
else if(line != null){
release = true;
}
//Last, If buffer is null, wait and read again. if still null, assume client closed connection
else{
sleep(500);
line = bufferedReader.readLine();
if(line == null){
release = true;
}
}
}
//Release the current client
if (release) {
clientSocket.close();
clientSocket = null;
}
}
}
catch (SocketException e){
// Do nothing, this exception is expected when another thread causes closure of serverSocket and
// this client socket is still accepting.
}
catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}