The code below is not exceptional in itself…however, the comments that were provided by a friend are extremely exceptional and well worth the read!
/**********************************************************
* Student Name: Benaiah Henry
* Course: COSC 4303
* Package: Server
* File name: ConnectionHandler.java
*
* Purpose:
*
* Development Computer: Dell Dimenstion E520
* Operating System: Ubuntu 9.10
* Integrated Development Environment (IDE): Netbeans IDE 6.7.1
* Compiler: javac
**************************************************************/
import java.net.*;
import java.io.*;
import java.util.*;
/**
* This handles the connection (not MANAGE, HANDLE) Management is an entirely
* different class which probably involves lots of pointy ears, walking around,
* and looking for the token ring under the desk (it's very small and hard to
* find). But that's not this class.
*
* This class is more like Dogbert. It smacks you upside the head, tells you
* things you need to know but won't listen to, and walks away with the money.
* That's what happens when we throw an exception here - Dogbert takes the money.
* @author Benaiah Henry
*/
public class ConnectionHandler {
ServerSocket serverSocket;
ResultHandler resultHandler;
String serverIP;
ArrayList<ConnectedUser> connectedUsers;
//ArrayList<ServerSocket> serverSockets;
public static final int DEFAULT_PORT = 1234;
public static final int MAX_CLIENTS = 500;
public static final int ACCEPT_TIMEOUT_LENGTH = 10;
public static final int SHORT_STRING = 32;
/**
*
* @throws java.io.IOException
*/
public ConnectionHandler(ResultHandler resultH) {
resultHandler = resultH;
try {
connectedUsers = new ArrayList<ConnectedUser>();
//serverSockets = new ArrayList<ServerSocket>();
InetAddress localIP = InetAddress.getLocalHost();
serverIP = localIP.getHostAddress();
serverSocket = new ServerSocket(DEFAULT_PORT, MAX_CLIENTS);
serverSocket.setSoTimeout(5);
} catch (IOException e) {
System.err.println("Failed to create ServerSocket");
} // End Try/Catch
} // End Constructor
/*
* Have you ever been bothered by how none of the clocks anywhere agree
* with each other? Have you ever just wanted a short little method
* that would synchronize everything? That would just update and make
* standardized time work? Yes we can!
* But this is not that method...
*/
//**************************************************************/
/**
* Checks for new data from all clients
*/
public void update() {
ConnectedUser temp = null;
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
readFromClient(temp.getSocket(), temp.getDataInputStream(), temp.getDataOutputStream(), i);
} // End for
} // End update
//**************************************************************/
/**
* Checks a serverSocket for new client connections
* And Asks accounting department to cut check to our wonderful clients
*
*/
public void checkForClients() {
DataOutputStream out;
DataInputStream in;
Socket connection;
InetAddress tempIP;
String IP;
try {
connection = serverSocket.accept();
//connection.getChannel().configureBlocking(false);
//System.err.println("after connection made");
in = new DataInputStream(connection.getInputStream());
out = new DataOutputStream(connection.getOutputStream());
tempIP = connection.getInetAddress();
IP = tempIP.toString();
//System.err.println("after ip string");
// create a new user ex nihilo
connectedUsers.add(new ConnectedUser(IP, null, connection, in, out));
//System.err.println("after add user");
} catch (SocketTimeoutException e) {
System.err.println("accept timeout - continuing execution");
} catch (IOException e) {
System.err.println("socket accept failed");
} // End Try/Catch
} // End checkForClients
//**************************************************************/
/**
*
* @param connection The socket to read from
* @param in The DataInputStream of the connection
* @param out The DataOutputStream of the connection
* @param index Index of the client to read from
*/
public void readFromClient(Socket connection, DataInputStream in, DataOutputStream out, int index) {
int msgTypeID;
try {
connection.setSoTimeout(5);
msgTypeID = in.readInt();
switch (msgTypeID) {
case 100:
process100(in, index);
break;
case 300:
process300(in, index);
break;
case 900:
process900(index, connection, in, out);
break;
default:
System.err.println("Invalid message code");
break;
} // End switch
} catch (IOException e) {
System.err.println("No data from client");
} // End Try/Catch
}// End readFromClient
/**
* The wonderful processing method
* @param in A DataInputStream
* @param index Index of client
*/
private void process100(DataInputStream in, int index) {
String alias = null;
byte[] temp = new byte[SHORT_STRING];
try {
for (int i = 0; i < SHORT_STRING; i++) {
temp[i] = in.readByte();
}
alias = new String(temp).trim();
connectedUsers.get(index).setAlias(alias);
System.out.println(alias);
} catch (IOException e) {
System.err.println("Failed to process 100 message");
}
}
/**
* The other wonderful processing method, but 3 times better than the first one
* @param in A DataInputStream
* @param index Client index
*/
private void process300(DataInputStream in, int index) {
int questionNum = 0;
boolean didAnswer = false; // yes, I know this is bad grammar
int timeTaken = 0; // careful, there will be problems if the user takes longer than 49,710 days to answer
// if you think that's too long, consider using 'long'
String answer = null;
byte[] temp = new byte[SHORT_STRING];
try {
questionNum = in.readInt();
didAnswer = in.readBoolean();
timeTaken = in.readInt();
for (int i = 0; i < SHORT_STRING; i++) {
temp[i] = in.readByte();
} // End for
answer = new String(temp).trim();
} catch (Exception e) {
System.err.println("Failed to process 300 message");
} // End Try/catch
resultHandler.storeAnswer(questionNum, didAnswer, timeTaken, answer, connectedUsers.get(index).getIPAddress());
} // End process300
/*
* Some of you may be wondering why process900 sends 999. Let me explain:
* Process 0 was created.
* process 0 begot process 100. Process 0 lived for
* 425 seconds and had other sons and daughters.
* Process 100 begot process 200. Process 100 lived for
* 511 seconds and had other sons and daughters.
* Process 200 begot process 300. Process 200 lived for
* 441 seconds and had other sons and daughters.
* Process 300 begot process 400. Process 300 lived for
* 225 seconds and had other sons and daughters.
* Process 400 begot process 500. Process 500 lived for
* 311 seconds and had other sons and daughters.
* Process 500 begot process 600. Now process 500 lived in the days of
* Windows ME. Dark were the times, and wild ran the memory manager.
* Process 500 lived 23 seconds and had no other children. Then it seg-faulted.
* Process 600 begot process 700. Process 600 lived for
* 259 seconds and had other sons and daughters.
* Process 700 begot process 800, who was lost in the upgrade to Vista.
* Process 700 begot process 900 (after confirming "allow" 71942 times.
* Children can be quite a security risk). Process 700 lived for
* 111 seconds and had other sons and daughters.
*
* Now process 900 walked with the kernel, and had many sons
* and daughters. It was granted a large memory footprint, and the
* exclusive use of 2 CPU cores, plus direct access to the PCI bus.
* Having so much wealth to manage, Process 900 diligently instructed
* his children, giving to some 10mb, to other 100mb, and to some whole
* gigabytes of memory. Thus was process 999, son of process 900,
* alloted communication with the first 13 pins of the card in 2nd PCI
* slot (which happens to be the NIC), and is thus responsible for
* for sending poetry, free chicken sandwich coupons, and instructions
* on building to-scale parking garages on the kitchen table without
* creating multi-car pileups.
*/
//**************************************************************/
/**
*
* Closes the connections
* @param index Index of client to close connections for
* @param connection The socket of the client
* @param in DataInputStream of connection
* @param out DataOutputStream of connection
*/
private void process900(int index, Socket connection, DataInputStream in, DataOutputStream out) {
send999(connectedUsers.get(index).getAlias(), out);
try {
in.close();
out.close();
connection.close();
} catch (Exception e) {
System.err.printf("Failed to close connection for client at IP address: %s", connection.getInetAddress().toString());
} // End Try/Catch
} // End process900
//**************************************************************/
/**
* Send Connection has been closed
* @param alias Username of client
* @param out DataOutputStream of client
*/
private void send999(String alias, DataOutputStream out) {
try {
out.writeInt(999);
writeString(alias, SHORT_STRING, out);
} catch (IOException e) {
System.err.println("Failed to send 999 message");
} // End Try/Catch
} // End send999
/**
* Writes strings to a DataOutputStream. Either truncates at size, or pads with 0's to size.
*
* Cutting a string would be more fun, but such is life
*
* @param s
* @param size
* @param out
*/
private void writeString(String s, int size, DataOutputStream out) {
try {
if (s.length() < (size - 1)) {
out.writeBytes(s);
for (int i = 0; i < (size - s.length()); i++) {
out.writeByte(0);
} // End for
} else if (s.length() > (size - 1)) {
s = s.trim().substring(0, (size - 1));
out.writeBytes(s);
out.writeByte(0);
} else {
out.writeBytes(s);
out.writeByte(0);
} // End if/else if/else
} catch (IOException e) {
System.err.println("Write failed");
System.exit(-1);
} // End Try/Catch
} // End writeString
//**************************************************************/
/**
* Send the question to all clients
* @param questionNum Number of the question
* @param questionType Type of question as an int
* @param timeLimit Time limit on question
* @param tallyMode Force tally mode
* @param optionCount Number of answer options
* @param questionText The question
* @param answerOptions All answer options concatenated and separated by ascii "04"
*/
public void sendQuestion(int questionNum, int questionType, int timeLimit, boolean tallyMode, int optionCount, String questionText, String answerOptions) {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream();
out.writeInt(200);
out.writeInt(questionNum);
out.writeInt(questionType);
out.writeInt(timeLimit);
out.writeBoolean(tallyMode);
out.writeInt(optionCount);
writeString(questionText, 256, out);
writeString(answerOptions, 256, out);
} // End for
} catch (IOException e) {
System.err.printf("Failed to send 200 message to IP address %s", temp.getSocket().getInetAddress().toString());
} // End Try/Catch
} // End SendQuestion
//The llama in the sky has recieved your request
//but your question is judged as causing unrest
//the words are malicious
//puncuation sadicious
//we'll have to correct for success
//**************************************************************/
/**
* Send the correct answer to clients
* @param isCorrect Whether the answer was correct
* @param timeTaken How long it took to answer
* @param submittedAnswer Client's submitted answer
* @param correctAnswer Correct answer
*/
public void sendGradedQuestion(boolean isCorrect, int timeTaken, String submittedAnswer, String correctAnswer) {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream(); //check for actual function name
//we sent a 400 (I don't know why)
//I would have chosen 3,4, or pi
//but no one asked me
//or revealed the design
//no diagrams, no tree
//(they charged a $10 fine)
out.writeInt(400);
out.writeBoolean(isCorrect);
out.writeInt(timeTaken);
writeString(submittedAnswer, 32, out);
writeString(correctAnswer, 32, out);
} // End for
} catch (IOException e) {
System.err.println("Failed to send 400 message to all clients");
} // End Try/Catch
}// End sendGradedQuestion
//**************************************************************/
/**
* Send notification of end of set to all clients
*/
public void sendEndOfSet() {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream();
out.writeInt(500);
} // End for
} catch (IOException e) {
System.err.println("Failed to send 500 message to all clients");
} // End Try/Catch
}// End sendEndOfSet
//**************************************************************/
public String getServerIP() {
return serverIP;
} // End getServerIP
//**************************************************************/
public int getConnectedUserCount() {
return connectedUsers.size();
} // End getConnectedUserCount
}// End ConnectionHandler
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**********************************************************
* Student name: Benaiah Henry
* Course: COSC 4303
* Package: Client
* File name: ConnectionManager.java
*
* Purpose: Handles reading and writing to the network.
* Also passes information to controller to be give
* to the UI.
*
* Development Computer: Dell Dimenstion E520
* Operating System: Ubuntu 9.04
* Integrated Development Environment (IDE): Netbeans IDE 6.7.1
* Compiler: javac
//**************************************************************/
import java.net.*;
import java.io.*;
import java.util.*;
/**
* Handles network connection between client and server
*
* In the beginning was the connection. And the connection was with packets, but
* the connection was without packets. It was without packets in the beginning.
* Thus, we created this manager.
*
* @author Benaiah Henry
* @version e/pi
*/
public class ConnectionManager {
Socket clientSocket;
DataOutputStream out;
DataInputStream in;
BufferedReader inString;
Controller controller;
public static final int MAX = 32;
public static final int SHORT_STRING = 32;
public static final int LONG_STRING = 256;
/**
*
* @param controllerRef Reference to Controller
*/
public ConnectionManager(Controller controllerRef) {
controller = controllerRef;
} // End constructor
//**************************************************************/
/**
* Connect to the Clickermatic server
* @param address IP Address of server
* @param port Port on server
* @param alias Username
*/
public void logIn(String address, int port, String alias) {
try {
clientSocket = new Socket(address, port);
in = new DataInputStream(clientSocket.getInputStream());
inString = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new DataOutputStream(clientSocket.getOutputStream());
} catch (IOException e) {
System.err.println("Connection Failed");
System.exit(-1);
} // End Try/Catch
//thus spake the Grand Llama. And the earth was silent as the grand
//message broke forth upon the ocean of packets and soughdough bread (for
//the internet has been filled, since the beginning with bakery goodness).
sendHello(alias);
} // End logIn
//**************************************************************/
/**
* Send HELLO (100) packet
* @param alias Username ***Can we make it amphibious???
*/
private void sendHello(String alias) {
try {
out.writeInt(100);
writeString(alias, SHORT_STRING);
} catch (IOException e) {
System.err.println("Failed to send HELLO");
System.exit(-1);
} // End Try/Catch
} // End sendHello
//**************************************************************/
/**
* Send user answer to server
* @param questionNumber
* @param answeredInTimeLimit
* @param elapsedTime
* @param clientAnswer
*/
public void submitAnswer(int questionNumber, boolean answeredInTimeLimit,
int elapsedTime, String clientAnswer) {
try {
out.writeInt(300);
out.writeInt(questionNumber);
out.writeBoolean(answeredInTimeLimit);
out.writeInt(elapsedTime);
writeString(clientAnswer, SHORT_STRING);
} catch (IOException e) {
//alas!! The soughdough bread hath clogged the message! Retreat, and
//contemplate the freshness of your bread.
System.err.println("Failed to send 300 message");
//System.exit(-1);
} // End Try/Catch
} // End submitAnswer
//**************************************************************/
/**
* Read packet in from server, check msgTypeID and call correct process method.
*/
public void readFromServer() {
int msgTypeID;
try {
msgTypeID = in.readInt();
switch (msgTypeID) {
case 200:
process200();
break;
case 400:
process400();
break;
case 500:
process500();
break;
case 999:
process999();
break;
default:
System.err.println("Invalid message code");
break;
} // End switch
} catch (IOException e) {
System.err.println("Failed to read from server");
} // End Try/Catch
} // End readFromServer
/*
* Ah, the 200 process! Mightiest of all! How swift and majestic its
* response! Gaze upon the internet and tell me, Muse, is there any greater
* response than this? How gloriously bright are its 0s. How regal the
* leading 2! This, the response that launched a thousand ships to the
* harbors of Troy - tell me, Muse, of that story...
*/
//**************************************************************/
/**
* Recieve question from server and pass to controller.
*/
private void process200() {
//the question of no
//does not start with an 'o'
//though it's type be int
//it shall not repent
//and begin with zero
//(an unworthy foe)
int questionNo = 0;
int questionType = 0;
int timeLimit = 0;
//We stand united against tallying! Viva la revolution!
boolean tallyMode = false;
String questionText = null;
int optionCount = 0;
String answerOptions = null;
byte[] temp = new byte[LONG_STRING];
ArrayList<String> answerOptionsList = null;
try {
questionNo = in.readInt();
questionType = in.readInt();
timeLimit = in.readInt();
tallyMode = in.readBoolean();
for (int i = 0; i < LONG_STRING; i++) {
temp[i] = in.readByte();
} // End for
questionText = new String(temp).trim();
System.out.print(questionText);
optionCount = in.readInt();
for (int i = 0; i < LONG_STRING; i++) {
temp[i] = in.readByte();
} // End for
answerOptions = new String(temp).trim();
//System.out.print(answerOptions);
answerOptionsList = separateAnswers(answerOptions, optionCount);
} catch (IOException e) {
//thus passes the glory of the world...
System.err.println("Failed to process 200 message");
} // End Try/Catch
/**
* @see Controller
*/
controller.submitQuestionToUI(questionNo, questionType, timeLimit,
tallyMode, questionText, answerOptionsList);
} // End process200
//**************************************************************/
//mysterious murky
//succesful data suicide
//unfortunate depressing heart-wrenching callamitive
//failure
/**
* Recieve question answer from server and pass to controller
*/
private void process400() {
String submittedAnswer = null;
String correctAnswer = null;
boolean isRightAnswer = false;
int timeTaken = 0;
byte[] temp = new byte[LONG_STRING];
try {
isRightAnswer = in.readBoolean();
timeTaken = in.readInt();
for (int i = 0; i < LONG_STRING; i++) {
temp[i] = in.readByte();
} // End for
submittedAnswer = new String(temp).trim();
for (int i = 0; i < LONG_STRING; i++) {
temp[i] = in.readByte();
} // End for
correctAnswer = new String(temp);
} catch (IOException e) {
System.err.println("Failed to process 400 message");
// System.exit(-1);
} // End Try/Catch
/**
* @see Controller
*/
controller.submitAnswerToUI(submittedAnswer, correctAnswer, isRightAnswer,
timeTaken);
} // End process400
/*
* You be server errored! Errors upon you! Bwuahahaha!!!
*/
//**************************************************************/
/**
* Send notification of end of question set to controller
*/
private void process500() {
controller.endOfSet();
} // End process500
//**************************************************************/
/*
* We're sorry: The pipe seems to be clogged with sheep. If this is the first
* time you are experiencing this problem, please wait a minute and try
* again. If this is a recurring error, please contact 1-800-BAH-NOSHEEP.
*/
/**
* Close read/write streams and socket.
*/
private void process999() {
try {
out.close();
in.close();
inString.close();
clientSocket.close();
controller.disconnectReceived();
} catch (IOException E) {
System.err.println("Failed to close connection");
// System.exit(-1);
} // End Try/Catch
} // End process 999
//**************************************************************/
/**
* Write a string to a packet.
* Ensures that <code>s</code> is exactly <code>size</code>.
* <code> s.length > size</code>, truncate
* <code> s.length < size</code>, pad to <code>size</code> with 0's
* @param s String to send
* @param size Desired length of string
*/
private void writeString(String s, int size) {
try {
if (s.length() < (size - 1)) {
out.writeBytes(s);
for (int i = 0; i < (size - s.length()); i++) {
out.writeByte(0);
} // End for
} else if (s.length() > (size - 1)) {
s = s.trim().substring(0, (size - 1));
out.writeBytes(s);
out.writeByte(0);
} else {
out.writeBytes(s);
out.writeByte(0);
} //End if/else if/else
} catch (IOException e) {
//ah, but read succeed!
System.err.println("Write failed");
// System.exit(-1);
} // End Try/Catch
} // End writeString
/*
* We thought about combining the answers, but it went something like this:
* Scenario 1:
* Joe Cool: Question?
* Random teenager: Answer!!
* Joe Cool: yay! that made sense!
* Random teenager: Answer part 2!!
* Joe Cool: Wait, what? You already answered!
* Random teenager: Answer part 3!!
* Joe Cool: Hey - that contradicted!
* Random teenager: Extremely long and confusing answer part 4!!
* Joe Cool: (forgetting parts 1,2, and 3) oh, that makes sense!
*
* Scenario 2:
* Joe Cool: Question?
* Random teenager: Answer parts 1,2,3, and extremely long and confusing
* answer part 4!!!
* Joe Cool: (brain explodes)
*
* So you see, seperate answers avoid exploading heads. RAs hate those.
*/
//**************************************************************/
/**
* Separate input string into separate answer strings.
* Answer's within the input string are separated by ASCII value 04.
* @param input Compound string
* @param number Number of answers in input string
* @return ArrayList<String> of correct answers
*/
private ArrayList<String> separateAnswers(String input, int number) {
int start = 0;
int index;
String temp;
ArrayList<String> answerOptions = new ArrayList<String>();
for (int i = 0; i < number; i++) {
index = input.indexOf(4, start);
temp = input.substring(start, index);
start = index + 1;
answerOptions.add(temp);
} // End for
return answerOptions;
} // End separateAnswers
/*
* Hello, I would really like to stop talking to you. Honestly, I feel like
* I've wasted the last hour of my life. I feel dummer for talking to you.
* This conversation has done nothing but contribute to the heat death of
* the universe, and you know what? My CPU is already over 200 degrees!! So
* yeah. 900 to you.
*/
//**************************************************************/
/**
* Send disconnect request to server
* @param alias Username
*/
public void sendDisconnectRequest(String alias) {
try {
out.writeInt(900);
out.writeBytes(alias);
} catch (IOException e) {
System.err.println("Failed to send 900 message");
//System.exit(-1);
} // End Try/Catch
} // End sendDisconnectRequest
//**************************************************************/
/**
* Chekck if client socket is open.
* @return <code>true</code> if socket is connected.
*/
public boolean isOpen() {
//it's open, but is anything on sale?
return clientSocket.isConnected();
} //End isOpen
} // End ConnectionManager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**********************************************************
* Student name: Benaiah Henry
* Course: COSC
* Package: Server
* File name: ConnectionHandler.java
* Purpose: Thread for checking the network connection on
* the server by calling the server update() function.
*
* Assumptions: None known.
*
* Limitations: None known.
*
* Development Computer: Dell Dimenstion E520
* Operating System: Ubuntu 9.0
* Integrated Development Environment (IDE): Netbeans IDE 6.7.1
* Compiler: javac
**************************************************************/
/**
* This class checks the network for (among other things) hamsters, wild rats,
* Bull Wievels, your old roomate, and spinach (seriously, somebody has to check
* for spinach). It doesn't return anything if it finds them, so I assume it
* removes them, but judging by the length of this class, I'm betting it just
* points and laughs.
* @author benaiah
*/
public class CheckNetwork extends Thread {
ConnectionHandler connection;
CheckNetwork(ConnectionHandler connectionH) {
connection = connectionH;
}
@Override
public void run() {
while (true) {
connection.checkForClients();
connection.update();
try {
//point and laugh
wait(100);
} catch (InterruptedException e) {
//that's right. This computer is impatient.
//can't even wait around for a bunny to hop
//away. Although, the old roomates CAN be hard
//to move...
System.out.println("Wait failed");
}
}
}
} //the final parenthesis, golden and sweet: End of class is so neat!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**********************************************************
* Student Name: Benaiah Henry
* Course: COSC 4303
* Package: Server
* File name: ConnectionHandler.java
*
* Purpose:
*
* Development Computer: Dell Dimenstion E520
* Operating System: Ubuntu 9.10
* Integrated Development Environment (IDE): Netbeans IDE 6.7.1
* Compiler: javac
**************************************************************/
import java.net.*;
import java.io.*;
import java.util.*;
/**
* This handles the connection (not MANAGE, HANDLE) Management is an entirely
* different class which probably involves lots of pointy ears, walking around,
* and looking for the token ring under the desk (it’s very small and hard to
* find). But that’s not this class.
*
* This class is more like Dogbert. It smacks you upside the head, tells you
* things you need to know but won’t listen to, and walks away with the money.
* That’s what happens when we throw an exception here – Dogbert takes the money.
* @author Benaiah Henry
*/
public class ConnectionHandler {
ServerSocket serverSocket;
ResultHandler resultHandler;
String serverIP;
ArrayList<ConnectedUser> connectedUsers;
//ArrayList<ServerSocket> serverSockets;
public static final int DEFAULT_PORT = 1234;
public static final int MAX_CLIENTS = 500;
public static final int ACCEPT_TIMEOUT_LENGTH = 10;
public static final int SHORT_STRING = 32;
/**
*
* @throws java.io.IOException
*/
public ConnectionHandler(ResultHandler resultH) {
resultHandler = resultH;
try {
connectedUsers = new ArrayList<ConnectedUser>();
//serverSockets = new ArrayList<ServerSocket>();
InetAddress localIP = InetAddress.getLocalHost();
serverIP = localIP.getHostAddress();
serverSocket = new ServerSocket(DEFAULT_PORT, MAX_CLIENTS);
serverSocket.setSoTimeout(5);
} catch (IOException e) {
System.err.println(“Failed to create ServerSocket”);
} // End Try/Catch
} // End Constructor
/*
* Have you ever been bothered by how none of the clocks anywhere agree
* with each other? Have you ever just wanted a short little method
* that would synchronize everything? That would just update and make
* standardized time work? Yes we can!
* But this is not that method…
*/
//**************************************************************/
/**
* Checks for new data from all clients
*/
public void update() {
ConnectedUser temp = null;
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
readFromClient(temp.getSocket(), temp.getDataInputStream(), temp.getDataOutputStream(), i);
} // End for
} // End update
//**************************************************************/
/**
* Checks a serverSocket for new client connections
* And Asks accounting department to cut check to our wonderful clients
*
*/
public void checkForClients() {
DataOutputStream out;
DataInputStream in;
Socket connection;
InetAddress tempIP;
String IP;
try {
connection = serverSocket.accept();
//connection.getChannel().configureBlocking(false);
//System.err.println(“after connection made”);
in = new DataInputStream(connection.getInputStream());
out = new DataOutputStream(connection.getOutputStream());
tempIP = connection.getInetAddress();
IP = tempIP.toString();
//System.err.println(“after ip string”);
// create a new user ex nihilo
connectedUsers.add(new ConnectedUser(IP, null, connection, in, out));
//System.err.println(“after add user”);
} catch (SocketTimeoutException e) {
System.err.println(“accept timeout – continuing execution”);
} catch (IOException e) {
System.err.println(“socket accept failed”);
} // End Try/Catch
} // End checkForClients
//**************************************************************/
/**
*
* @param connection The socket to read from
* @param in The DataInputStream of the connection
* @param out The DataOutputStream of the connection
* @param index Index of the client to read from
*/
public void readFromClient(Socket connection, DataInputStream in, DataOutputStream out, int index) {
int msgTypeID;
try {
connection.setSoTimeout(5);
msgTypeID = in.readInt();
switch (msgTypeID) {
case 100:
process100(in, index);
break;
case 300:
process300(in, index);
break;
case 900:
process900(index, connection, in, out);
break;
default:
System.err.println(“Invalid message code”);
break;
} // End switch
} catch (IOException e) {
System.err.println(“No data from client”);
} // End Try/Catch
}// End readFromClient
/**
* The wonderful processing method
* @param in A DataInputStream
* @param index Index of client
*/
private void process100(DataInputStream in, int index) {
String alias = null;
byte[] temp = new byte[SHORT_STRING];
try {
for (int i = 0; i < SHORT_STRING; i++) {
temp[i] = in.readByte();
}
alias = new String(temp).trim();
connectedUsers.get(index).setAlias(alias);
System.out.println(alias);
} catch (IOException e) {
System.err.println(“Failed to process 100 message”);
}
}
/**
* The other wonderful processing method, but 3 times better than the first one
* @param in A DataInputStream
* @param index Client index
*/
private void process300(DataInputStream in, int index) {
int questionNum = 0;
boolean didAnswer = false; // yes, I know this is bad grammar
int timeTaken = 0; // careful, there will be problems if the user takes longer than 49,710 days to answer
// if you think that’s too long, consider using ‘long’
String answer = null;
byte[] temp = new byte[SHORT_STRING];
try {
questionNum = in.readInt();
didAnswer = in.readBoolean();
timeTaken = in.readInt();
for (int i = 0; i < SHORT_STRING; i++) {
temp[i] = in.readByte();
} // End for
answer = new String(temp).trim();
} catch (Exception e) {
System.err.println(“Failed to process 300 message”);
} // End Try/catch
resultHandler.storeAnswer(questionNum, didAnswer, timeTaken, answer, connectedUsers.get(index).getIPAddress());
} // End process300
/*
* Some of you may be wondering why process900 sends 999. Let me explain:
* Process 0 was created.
* process 0 begot process 100. Process 0 lived for
* 425 seconds and had other sons and daughters.
* Process 100 begot process 200. Process 100 lived for
* 511 seconds and had other sons and daughters.
* Process 200 begot process 300. Process 200 lived for
* 441 seconds and had other sons and daughters.
* Process 300 begot process 400. Process 300 lived for
* 225 seconds and had other sons and daughters.
* Process 400 begot process 500. Process 500 lived for
* 311 seconds and had other sons and daughters.
* Process 500 begot process 600. Now process 500 lived in the days of
* Windows ME. Dark were the times, and wild ran the memory manager.
* Process 500 lived 23 seconds and had no other children. Then it seg-faulted.
* Process 600 begot process 700. Process 600 lived for
* 259 seconds and had other sons and daughters.
* Process 700 begot process 800, who was lost in the upgrade to Vista.
* Process 700 begot process 900 (after confirming “allow” 71942 times.
* Children can be quite a security risk). Process 700 lived for
* 111 seconds and had other sons and daughters.
*
* Now process 900 walked with the kernel, and had many sons
* and daughters. It was granted a large memory footprint, and the
* exclusive use of 2 CPU cores, plus direct access to the PCI bus.
* Having so much wealth to manage, Process 900 diligently instructed
* his children, giving to some 10mb, to other 100mb, and to some whole
* gigabytes of memory. Thus was process 999, son of process 900,
* alloted communication with the first 13 pins of the card in 2nd PCI
* slot (which happens to be the NIC), and is thus responsible for
* for sending poetry, free chicken sandwich coupons, and instructions
* on building to-scale parking garages on the kitchen table without
* creating multi-car pileups.
*/
//**************************************************************/
/**
*
* Closes the connections
* @param index Index of client to close connections for
* @param connection The socket of the client
* @param in DataInputStream of connection
* @param out DataOutputStream of connection
*/
private void process900(int index, Socket connection, DataInputStream in, DataOutputStream out) {
send999(connectedUsers.get(index).getAlias(), out);
try {
in.close();
out.close();
connection.close();
} catch (Exception e) {
System.err.printf(“Failed to close connection for client at IP address: %s”, connection.getInetAddress().toString());
} // End Try/Catch
} // End process900
//**************************************************************/
/**
* Send Connection has been closed
* @param alias Username of client
* @param out DataOutputStream of client
*/
private void send999(String alias, DataOutputStream out) {
try {
out.writeInt(999);
writeString(alias, SHORT_STRING, out);
} catch (IOException e) {
System.err.println(“Failed to send 999 message”);
} // End Try/Catch
} // End send999
/**
* Writes strings to a DataOutputStream. Either truncates at size, or pads with 0′s to size.
*
* Cutting a string would be more fun, but such is life
*
* @param s
* @param size
* @param out
*/
private void writeString(String s, int size, DataOutputStream out) {
try {
if (s.length() < (size – 1)) {
out.writeBytes(s);
for (int i = 0; i < (size – s.length()); i++) {
out.writeByte(0);
} // End for
} else if (s.length() > (size – 1)) {
s = s.trim().substring(0, (size – 1));
out.writeBytes(s);
out.writeByte(0);
} else {
out.writeBytes(s);
out.writeByte(0);
} // End if/else if/else
} catch (IOException e) {
System.err.println(“Write failed”);
System.exit(-1);
} // End Try/Catch
} // End writeString
//**************************************************************/
/**
* Send the question to all clients
* @param questionNum Number of the question
* @param questionType Type of question as an int
* @param timeLimit Time limit on question
* @param tallyMode Force tally mode
* @param optionCount Number of answer options
* @param questionText The question
* @param answerOptions All answer options concatenated and separated by ascii “04″
*/
public void sendQuestion(int questionNum, int questionType, int timeLimit, boolean tallyMode, int optionCount, String questionText, String answerOptions) {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream();
out.writeInt(200);
out.writeInt(questionNum);
out.writeInt(questionType);
out.writeInt(timeLimit);
out.writeBoolean(tallyMode);
out.writeInt(optionCount);
writeString(questionText, 256, out);
writeString(answerOptions, 256, out);
} // End for
} catch (IOException e) {
System.err.printf(“Failed to send 200 message to IP address %s”, temp.getSocket().getInetAddress().toString());
} // End Try/Catch
} // End SendQuestion
//The llama in the sky has recieved your request
//but your question is judged as causing unrest
//the words are malicious
//puncuation sadicious
//we’ll have to correct for success
//**************************************************************/
/**
* Send the correct answer to clients
* @param isCorrect Whether the answer was correct
* @param timeTaken How long it took to answer
* @param submittedAnswer Client’s submitted answer
* @param correctAnswer Correct answer
*/
public void sendGradedQuestion(boolean isCorrect, int timeTaken, String submittedAnswer, String correctAnswer) {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream(); //check for actual function name
//we sent a 400 (I don’t know why)
//I would have chosen 3,4, or pi
//but no one asked me
//or revealed the design
//no diagrams, no tree
//(they charged a $10 fine)
out.writeInt(400);
out.writeBoolean(isCorrect);
out.writeInt(timeTaken);
writeString(submittedAnswer, 32, out);
writeString(correctAnswer, 32, out);
} // End for
} catch (IOException e) {
System.err.println(“Failed to send 400 message to all clients”);
} // End Try/Catch
}// End sendGradedQuestion
//**************************************************************/
/**
* Send notification of end of set to all clients
*/
public void sendEndOfSet() {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream();
out.writeInt(500);
} // End for
} catch (IOException e) {
System.err.println(“Failed to send 500 message to all clients”);
} // End Try/Catch
}// End sendEndOfSet
//**************************************************************/
public String getServerIP() {
return serverIP;
} // End getServerIP
//**************************************************************/
public int getConnectedUserCount() {
return connectedUsers.size();
} // End getConnectedUserCount
}// End ConnectionHandler
Exceptional Code Comments
The code below is not exceptional in itself…however, the comments that were provided by a friend are extremely exceptional and well worth the read!
* Student Name: Benaiah Henry
* Course: COSC 4303
* Package: Server
* File name: ConnectionHandler.java
*
* Purpose:
*
* Development Computer: Dell Dimenstion E520
* Operating System: Ubuntu 9.10
* Integrated Development Environment (IDE): Netbeans IDE 6.7.1
* Compiler: javac
**************************************************************/
import java.net.*;
import java.io.*;
import java.util.*;
/**
* This handles the connection (not MANAGE, HANDLE) Management is an entirely
* different class which probably involves lots of pointy ears, walking around,
* and looking for the token ring under the desk (it’s very small and hard to
* find). But that’s not this class.
*
* This class is more like Dogbert. It smacks you upside the head, tells you
* things you need to know but won’t listen to, and walks away with the money.
* That’s what happens when we throw an exception here – Dogbert takes the money.
* @author Benaiah Henry
*/
public class ConnectionHandler {
ServerSocket serverSocket;
ResultHandler resultHandler;
String serverIP;
ArrayList<ConnectedUser> connectedUsers;
//ArrayList<ServerSocket> serverSockets;
public static final int DEFAULT_PORT = 1234;
public static final int MAX_CLIENTS = 500;
public static final int ACCEPT_TIMEOUT_LENGTH = 10;
public static final int SHORT_STRING = 32;
/**
*
* @throws java.io.IOException
*/
public ConnectionHandler(ResultHandler resultH) {
resultHandler = resultH;
try {
connectedUsers = new ArrayList<ConnectedUser>();
//serverSockets = new ArrayList<ServerSocket>();
InetAddress localIP = InetAddress.getLocalHost();
serverIP = localIP.getHostAddress();
serverSocket = new ServerSocket(DEFAULT_PORT, MAX_CLIENTS);
serverSocket.setSoTimeout(5);
} catch (IOException e) {
System.err.println(“Failed to create ServerSocket”);
} // End Try/Catch
} // End Constructor
/*
* Have you ever been bothered by how none of the clocks anywhere agree
* with each other? Have you ever just wanted a short little method
* that would synchronize everything? That would just update and make
* standardized time work? Yes we can!
* But this is not that method…
*/
//**************************************************************/
/**
* Checks for new data from all clients
*/
public void update() {
ConnectedUser temp = null;
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
readFromClient(temp.getSocket(), temp.getDataInputStream(), temp.getDataOutputStream(), i);
} // End for
} // End update
//**************************************************************/
/**
* Checks a serverSocket for new client connections
* And Asks accounting department to cut check to our wonderful clients
*
*/
public void checkForClients() {
DataOutputStream out;
DataInputStream in;
Socket connection;
InetAddress tempIP;
String IP;
try {
connection = serverSocket.accept();
//connection.getChannel().configureBlocking(false);
//System.err.println(“after connection made”);
in = new DataInputStream(connection.getInputStream());
out = new DataOutputStream(connection.getOutputStream());
tempIP = connection.getInetAddress();
IP = tempIP.toString();
//System.err.println(“after ip string”);
// create a new user ex nihilo
connectedUsers.add(new ConnectedUser(IP, null, connection, in, out));
//System.err.println(“after add user”);
} catch (SocketTimeoutException e) {
System.err.println(“accept timeout – continuing execution”);
} catch (IOException e) {
System.err.println(“socket accept failed”);
} // End Try/Catch
} // End checkForClients
//**************************************************************/
/**
*
* @param connection The socket to read from
* @param in The DataInputStream of the connection
* @param out The DataOutputStream of the connection
* @param index Index of the client to read from
*/
public void readFromClient(Socket connection, DataInputStream in, DataOutputStream out, int index) {
int msgTypeID;
try {
connection.setSoTimeout(5);
msgTypeID = in.readInt();
switch (msgTypeID) {
case 100:
process100(in, index);
break;
case 300:
process300(in, index);
break;
case 900:
process900(index, connection, in, out);
break;
default:
System.err.println(“Invalid message code”);
break;
} // End switch
} catch (IOException e) {
System.err.println(“No data from client”);
} // End Try/Catch
}// End readFromClient
/**
* The wonderful processing method
* @param in A DataInputStream
* @param index Index of client
*/
private void process100(DataInputStream in, int index) {
String alias = null;
byte[] temp = new byte[SHORT_STRING];
try {
for (int i = 0; i < SHORT_STRING; i++) {
temp[i] = in.readByte();
}
alias = new String(temp).trim();
connectedUsers.get(index).setAlias(alias);
System.out.println(alias);
} catch (IOException e) {
System.err.println(“Failed to process 100 message”);
}
}
/**
* The other wonderful processing method, but 3 times better than the first one
* @param in A DataInputStream
* @param index Client index
*/
private void process300(DataInputStream in, int index) {
int questionNum = 0;
boolean didAnswer = false; // yes, I know this is bad grammar
int timeTaken = 0; // careful, there will be problems if the user takes longer than 49,710 days to answer
// if you think that’s too long, consider using ‘long’
String answer = null;
byte[] temp = new byte[SHORT_STRING];
try {
questionNum = in.readInt();
didAnswer = in.readBoolean();
timeTaken = in.readInt();
for (int i = 0; i < SHORT_STRING; i++) {
temp[i] = in.readByte();
} // End for
answer = new String(temp).trim();
} catch (Exception e) {
System.err.println(“Failed to process 300 message”);
} // End Try/catch
resultHandler.storeAnswer(questionNum, didAnswer, timeTaken, answer, connectedUsers.get(index).getIPAddress());
} // End process300
/*
* Some of you may be wondering why process900 sends 999. Let me explain:
* Process 0 was created.
* process 0 begot process 100. Process 0 lived for
* 425 seconds and had other sons and daughters.
* Process 100 begot process 200. Process 100 lived for
* 511 seconds and had other sons and daughters.
* Process 200 begot process 300. Process 200 lived for
* 441 seconds and had other sons and daughters.
* Process 300 begot process 400. Process 300 lived for
* 225 seconds and had other sons and daughters.
* Process 400 begot process 500. Process 500 lived for
* 311 seconds and had other sons and daughters.
* Process 500 begot process 600. Now process 500 lived in the days of
* Windows ME. Dark were the times, and wild ran the memory manager.
* Process 500 lived 23 seconds and had no other children. Then it seg-faulted.
* Process 600 begot process 700. Process 600 lived for
* 259 seconds and had other sons and daughters.
* Process 700 begot process 800, who was lost in the upgrade to Vista.
* Process 700 begot process 900 (after confirming “allow” 71942 times.
* Children can be quite a security risk). Process 700 lived for
* 111 seconds and had other sons and daughters.
*
* Now process 900 walked with the kernel, and had many sons
* and daughters. It was granted a large memory footprint, and the
* exclusive use of 2 CPU cores, plus direct access to the PCI bus.
* Having so much wealth to manage, Process 900 diligently instructed
* his children, giving to some 10mb, to other 100mb, and to some whole
* gigabytes of memory. Thus was process 999, son of process 900,
* alloted communication with the first 13 pins of the card in 2nd PCI
* slot (which happens to be the NIC), and is thus responsible for
* for sending poetry, free chicken sandwich coupons, and instructions
* on building to-scale parking garages on the kitchen table without
* creating multi-car pileups.
*/
//**************************************************************/
/**
*
* Closes the connections
* @param index Index of client to close connections for
* @param connection The socket of the client
* @param in DataInputStream of connection
* @param out DataOutputStream of connection
*/
private void process900(int index, Socket connection, DataInputStream in, DataOutputStream out) {
send999(connectedUsers.get(index).getAlias(), out);
try {
in.close();
out.close();
connection.close();
} catch (Exception e) {
System.err.printf(“Failed to close connection for client at IP address: %s”, connection.getInetAddress().toString());
} // End Try/Catch
} // End process900
//**************************************************************/
/**
* Send Connection has been closed
* @param alias Username of client
* @param out DataOutputStream of client
*/
private void send999(String alias, DataOutputStream out) {
try {
out.writeInt(999);
writeString(alias, SHORT_STRING, out);
} catch (IOException e) {
System.err.println(“Failed to send 999 message”);
} // End Try/Catch
} // End send999
/**
* Writes strings to a DataOutputStream. Either truncates at size, or pads with 0′s to size.
*
* Cutting a string would be more fun, but such is life
*
* @param s
* @param size
* @param out
*/
private void writeString(String s, int size, DataOutputStream out) {
try {
if (s.length() < (size – 1)) {
out.writeBytes(s);
for (int i = 0; i < (size – s.length()); i++) {
out.writeByte(0);
} // End for
} else if (s.length() > (size – 1)) {
s = s.trim().substring(0, (size – 1));
out.writeBytes(s);
out.writeByte(0);
} else {
out.writeBytes(s);
out.writeByte(0);
} // End if/else if/else
} catch (IOException e) {
System.err.println(“Write failed”);
System.exit(-1);
} // End Try/Catch
} // End writeString
//**************************************************************/
/**
* Send the question to all clients
* @param questionNum Number of the question
* @param questionType Type of question as an int
* @param timeLimit Time limit on question
* @param tallyMode Force tally mode
* @param optionCount Number of answer options
* @param questionText The question
* @param answerOptions All answer options concatenated and separated by ascii “04″
*/
public void sendQuestion(int questionNum, int questionType, int timeLimit, boolean tallyMode, int optionCount, String questionText, String answerOptions) {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream();
out.writeInt(200);
out.writeInt(questionNum);
out.writeInt(questionType);
out.writeInt(timeLimit);
out.writeBoolean(tallyMode);
out.writeInt(optionCount);
writeString(questionText, 256, out);
writeString(answerOptions, 256, out);
} // End for
} catch (IOException e) {
System.err.printf(“Failed to send 200 message to IP address %s”, temp.getSocket().getInetAddress().toString());
} // End Try/Catch
} // End SendQuestion
//The llama in the sky has recieved your request
//but your question is judged as causing unrest
//the words are malicious
//puncuation sadicious
//we’ll have to correct for success
//**************************************************************/
/**
* Send the correct answer to clients
* @param isCorrect Whether the answer was correct
* @param timeTaken How long it took to answer
* @param submittedAnswer Client’s submitted answer
* @param correctAnswer Correct answer
*/
public void sendGradedQuestion(boolean isCorrect, int timeTaken, String submittedAnswer, String correctAnswer) {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream(); //check for actual function name
//we sent a 400 (I don’t know why)
//I would have chosen 3,4, or pi
//but no one asked me
//or revealed the design
//no diagrams, no tree
//(they charged a $10 fine)
out.writeInt(400);
out.writeBoolean(isCorrect);
out.writeInt(timeTaken);
writeString(submittedAnswer, 32, out);
writeString(correctAnswer, 32, out);
} // End for
} catch (IOException e) {
System.err.println(“Failed to send 400 message to all clients”);
} // End Try/Catch
}// End sendGradedQuestion
//**************************************************************/
/**
* Send notification of end of set to all clients
*/
public void sendEndOfSet() {
ConnectedUser temp = null;
DataOutputStream out;
try {
for (int i = 0; i < connectedUsers.size(); i++) {
temp = connectedUsers.get(i);
out = temp.getDataOutputStream();
out.writeInt(500);
} // End for
} catch (IOException e) {
System.err.println(“Failed to send 500 message to all clients”);
} // End Try/Catch
}// End sendEndOfSet
//**************************************************************/
public String getServerIP() {
return serverIP;
} // End getServerIP
//**************************************************************/
public int getConnectedUserCount() {
return connectedUsers.size();
} // End getConnectedUserCount
}// End ConnectionHandler