package Chatroom;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
/**
* This is the main server class. It is the core of the server functionality, and the listener for incoming clients that will
* spawn threads to deal with clients, track their information, change their states and ultimately, clean up after all clients by keeping the
* data structures up to date.
* @version 0.1
*
*/
public class Server {
public static final int PORT = 9001; //fixed port to be used by the server for now
/**
* The set of all names of clients in the main room.
*/
private static HashSet<String> names = new HashSet<String>();
/**
* The set of all the print writers for all the clients. This is kept so we can easily broadcast messages.
*/
private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>();
/**
* The application\'s main method, which listens on the specified port and spawns handler threads
*/
public static void main(String[] args) throws Exception {
ServerLogger.log("Server started.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
/**
* A handler thread class. Handlers are spawned from the listening loop and are responsible for
* dealing with a single client and broadcasting its messages.
*/
private static class Handler extends Thread {
private String name; //Holds the username of this client only after authenticating.
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public User user;
private UserState state; //The state that the user is currently in. This is used for masking certain actions, and only allow those actions appropriate for
//this user state. For example a user in a game cannot accept invitations by other players. A user in the lobby cannot make a player move command.
/**
* Constructs a handler thread. All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket; //the supplied Socket is the one made when the listener accepted a client.
}
public void run() {
try {
//Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
//First, authenticate user and keep his user details.
while(true) {
out.println( "" + OpCodeSC.AUTHENTICATE.ordinal()); //Ask for authentication
String response = in.readLine(); //Read client response (should contain user and pass). User can disconnect at this point.
int opCode = Integer.parseInt(response.split("-|")[0]);
String subString = response.split("-|")[1];
if (opCode == OpCodeCS.AUTHENTICATE.ordinal() && MessageHandler.dispatch(opCode, subString, null) == true){
out.println("Authentication succesful.");
break;
}
else {
out.println("Authentication not succesful.");
}
//TODO: Fix to ensure proper user states and corresponding masking vectors for allowed actions.
}
//Second, keep accepting client messages, handling them, and sending responses.
while(true){
String response = in.readLine();
//Dissasemble and handle all client input.
int opcode = Integer.parseInt(response.split("-|")[0]);
String subString = response.split("-|")[1];
MessageHandler.dispatch(opcode, subString, null);
}
} catch (IOException e) {
ServerLogger.log(e.toString());
} finally {
//This client is going down. Remove its name and print writer from the sets, and close its socket.
if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
ServerLogger.log("Client closed connection.");
try {
socket.close();
} catch (IOException e) {
ServerLogger.log("Failed closing socket for a client that disconnected");
}
}
}
}
}