Initial commit
This commit is contained in:
commit
bf7100b28d
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*class
|
||||
*log
|
28
Main.java
Normal file
28
Main.java
Normal 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
181
Server.java
Normal 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
124
SocketThread.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user