Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import java.io.*;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.*;
- import static java.lang.System.exit;
- //Handles all other output threads -- tells them what to say
- class MasterOutputThread implements Runnable {
- private Set<OutputThread> outThreads;
- private Thread t;
- MasterOutputThread() {
- this.outThreads = new HashSet<>();
- }
- public void run() {
- Scanner console = new Scanner(System.in);
- while(true) {
- String text = console.nextLine();
- System.out.println("Received text " + text + " in master.");
- Iterator<OutputThread> it = this.outThreads.iterator();
- while (it.hasNext()) {
- it.next().sendText("Server announcement: " + text);
- }
- }
- }
- void start() {
- if(this.t == null) {
- t = new Thread(this);
- t.start();
- }
- }
- void addThreadToSet(OutputThread thread) {
- this.outThreads.add(thread);
- }
- void removeThreadFromSet(OutputThread thread) {
- this.outThreads.remove(thread);
- }
- public Set<OutputThread> threads() {
- return this.outThreads;
- }
- void forwardTextToOutputThreads(String text){
- Iterator<OutputThread> it = this.outThreads.iterator();
- while (it.hasNext()) {
- it.next().sendText(text);
- }
- }
- }
- //Thread class to handle output -- sending data to client
- class OutputThread implements Runnable {
- //Instance variables
- private Socket socket;
- private Thread t;
- private DataOutputStream output;
- private String userName;
- //Constructor
- OutputThread(Socket s) {
- this.socket = s;
- this.output = null;
- try {
- output = new DataOutputStream(this.socket.getOutputStream());
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- }
- public void run() {
- //Program loop -- doesn't actually do anything. Just keeps the thread running so that the sendText method can be called.
- while(true) {
- }
- }
- void start() {
- if(this.t == null) {
- t = new Thread(this);
- t.start();
- }
- }
- //Send text to the client.
- void sendText(String text) {
- System.out.println("Sending text " + text + " to " + this.userName + ".");
- try {
- this.output.writeUTF(text);
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- }
- void kill() {
- t.interrupt();
- try {
- this.socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- }
- void setUserName(String user) {
- this.userName = user;
- }
- }
- //Collection of all input threads
- class MasterInputThread implements Runnable {
- class User {
- private String userName;
- private String password;
- User(String user, String pass){
- this.userName = user;
- this.password = pass;
- }
- String userName(){
- return this.userName;
- }
- String password(){
- return this.password;
- }
- }
- //Instance variables
- private Thread t;
- private Set<InputThread> inThreads;
- private Set<User> users;
- //Helper method to split a line from the file into a user object. Assumes the line has a colon separating the username and password.
- private User convertTextToUser(String line){
- int i = 0;
- while (i < line.length() && line.charAt(i) != ':') {
- i++;
- }
- String user = line.substring(0, i);
- String pass = line.substring(i+1);
- return new User(user, pass);
- }
- private Set<User> loadUsersFromFile(String filename){
- Set<User> result = new HashSet<>();
- //Set up the file reader
- BufferedReader fileIn = null;
- boolean fileExists = true;
- try {
- fileIn = new BufferedReader(new FileReader(filename));
- } catch (FileNotFoundException e) {
- //Mark that file doesn't exist. If there is no file then we just want the set of users to be an empty set.
- fileExists = false;
- }
- //Load the text into user objects.
- if (fileExists) {
- try {
- String line = fileIn.readLine();
- while (line != null) {
- result.add(convertTextToUser(line));
- line = fileIn.readLine();
- }
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- }
- return result;
- }
- private void saveUsersToFile(String filename){
- //Set up the file output
- PrintWriter fileOut = null;
- try {
- fileOut = new PrintWriter(filename, "UTF-8");
- } catch (IOException e){
- e.printStackTrace();
- exit(1);
- }
- //Get the list of users and print each to the file. Each will be on its own line, with a colon separating
- //the username and password
- Iterator<User> it = this.users.iterator();
- while(it.hasNext()){
- User u = it.next();
- fileOut.println(u.userName() + ":" + u.password());
- }
- //Close the file
- fileOut.close();
- }
- MasterInputThread(){
- this.inThreads = new HashSet<>();
- this.users = loadUsersFromFile(System.getProperty("user.dir") + "/users.txt");
- }
- public void run(){
- //Program loop -- doesn't actually do anything other than keep the thread running.
- while(true){
- }
- }
- void start(){
- if(this.t == null) {
- t = new Thread(this);
- t.start();
- }
- }
- void addThreadToSet(InputThread thread) {
- this.inThreads.add(thread);
- }
- void removeThreadFromSet(InputThread thread) {
- this.inThreads.remove(thread);
- }
- public Set<InputThread> threads() {
- return this.inThreads;
- }
- void registerUser(String user, String pass){
- this.users.add(new User(user, pass));
- this.saveUsersToFile(System.getProperty("user.dir") + "/users.txt");
- }
- //Method to check if the specified username already exists
- boolean checkIfUserExists(String user){
- Iterator<User> it = this.users.iterator();
- while(it.hasNext()){
- User u = it.next();
- if (u.userName().equals(user)){
- return true;
- }
- }
- return false;
- }
- //Method to check if a given password matches the one on file for a user
- boolean checkPassword(String user, String pass) {
- Iterator<User> it = this.users.iterator();
- User desiredUser = null;
- while (it.hasNext() && desiredUser == null){
- User u = it.next();
- if (u.userName().equals(user)){
- desiredUser = u;
- }
- }
- return desiredUser.password().equals(pass);
- }
- }
- //Thread class to handle input -- receiving data from client
- class InputThread implements Runnable {
- //Instance variables
- private Socket socket;
- private Thread t;
- private OutputThread correspondingThread;
- private MasterOutputThread masterOut;
- private MasterInputThread masterIn;
- private String userName;
- //Constructor.
- InputThread(Socket s, OutputThread thread, MasterOutputThread masterOutThread, MasterInputThread masterInThread) {
- this.socket = s;
- this.correspondingThread = thread;
- this.masterOut = masterOutThread;
- this.masterIn = masterInThread;
- }
- //Create a list of strings that the server will treat as user commands. This is done as a set rather than a static
- //collection to support expansion with more commands.
- private Set<String> createSetOfCommands() {
- Set<String> result = new HashSet<>();
- result.add("/users");
- result.add("/logout");
- result.add("/help");
- result.add("/msg");
- return result;
- }
- //Create a map that has definitions of what each command does.
- private Map<String, String> createMapOfDescriptions() {
- Map<String, String> result = new HashMap<>();
- result.put("/users", " - Gives a list of currently logged in users");
- result.put("/logout", " - Log out of the chat");
- result.put("/help", " - Shows this list of commands");
- result.put("/msg", " <user> <message> - Sends a private message to a specified user");
- return result;
- }
- private String checkForCommand(Set<String> commands, String text) {
- Iterator<String> it = commands.iterator();
- while (it.hasNext()) {
- String command = it.next();
- if (text.startsWith(command)) {
- return command;
- }
- }
- return "";
- }
- private String[] splitPrivateMessage(String text) {
- String[] result = new String[3];
- int i = 4, j = 5;
- //Store the /msg part
- result[0] = text.substring(0, i);
- //Store the user name
- while (j < text.length() && text.charAt(j) != ' ') {
- j++;
- }
- result[1] = text.substring(i+1, j);
- //Store the message
- result[2] = text.substring(j+1);
- return result;
- }
- private boolean sendMessageIfUserOnline(String name, String message) {
- Iterator<InputThread> it = this.masterIn.threads().iterator();
- while (it.hasNext()) {
- InputThread user = it.next();
- if (name.equals(user.userName())) {
- user.correspondingThread().sendText(message);
- return true;
- }
- }
- return false;
- }
- public void run() {
- //Set up connection variables
- DataInputStream input = null;
- try {
- input = new DataInputStream(new BufferedInputStream(this.socket.getInputStream()));
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- //Determine whether the user is a new user or not
- String inputText = "a";
- while (!inputText.equals("1") && !inputText.equals("2")){
- try {
- inputText = input.readUTF();
- } catch (EOFException e) {
- //Do nothing
- } catch(IOException e){
- e.printStackTrace();
- exit(1);
- }
- if (!inputText.equals("1") && !inputText.equals("2")){
- this.correspondingThread.sendText("Input not recognized.");
- }
- }
- //String inputText will be "2" if new user, else they will be an existing user
- boolean newUser = inputText.equals("2");
- boolean authenticationComplete = false;
- while (!authenticationComplete) {
- //Get the username
- inputText = "a";
- if (newUser) {
- boolean acceptableUsername = false;
- if (!acceptableUsername || inputText.contains(":")) {
- try {
- inputText = input.readUTF();
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- //Tell the user if there is anything wrong with their new username
- if (inputText.contains(":")) {
- this.correspondingThread.sendText("Usernames cannot contain ':'. Please try again.");
- continue;
- } else if (this.masterIn.checkIfUserExists(inputText)) {
- this.correspondingThread.sendText("Username already exists. Please try again.");
- continue;
- } else {
- acceptableUsername = true;
- }
- }
- } else {
- boolean nameExists = false;
- if (!nameExists) {
- try {
- inputText = input.readUTF();
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- if (this.masterIn.checkIfUserExists(inputText)) {
- nameExists = true;
- } else {
- this.correspondingThread.sendText("Username does not exist. Try again.");
- continue;
- }
- }
- }
- this.userName = inputText;
- this.correspondingThread.setUserName(inputText);
- //Get the password
- this.correspondingThread.sendText("Enter password:");
- try {
- inputText = input.readUTF();
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- //Check if the user entered "" as a password
- if (inputText.equals("")) {
- this.correspondingThread.sendText("Password must be greater than 0 characters.");
- continue;
- }
- //Confirm the password if new user, else check if the password matches the one on file
- if (newUser) {
- String confirm = null;
- if (confirm == null || !inputText.equals(confirm)) {
- this.correspondingThread.sendText("Confirm password:");
- System.out.println(inputText);
- try {
- confirm = input.readUTF();
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- System.out.println(confirm);
- if (!inputText.equals(confirm)) {
- this.correspondingThread.sendText("Passwords do not match. Try again.");
- continue;
- } else {
- this.correspondingThread.sendText("Welcome to the chat! Type /help for a list of commands.");
- authenticationComplete = true;
- }
- }
- this.masterIn.registerUser(this.userName, inputText);
- } else {
- if (masterIn.checkPassword(this.userName, inputText)) {
- this.correspondingThread.sendText("Welcome to the chat! Type /help for a list of commands.");
- authenticationComplete = true;
- } else {
- this.correspondingThread.sendText("Authentication failed.");
- continue;
- }
- }
- }
- //Alert other users that someone has logged on.
- this.masterOut.forwardTextToOutputThreads("User " + this.userName + " has logged on.");
- //Add the corresponding output thread to the list of threads that get messages delivered.
- this.masterOut.addThreadToSet(this.correspondingThread);
- //Get the list of valid commands and their descriptions.
- Set<String> commands = createSetOfCommands();
- Map<String, String> descriptions = createMapOfDescriptions();
- //Program loop. Echo text to the server. Forward the text to the master output thread.
- inputText = "a";
- while (!checkForCommand(commands, inputText).equals("/logout")) {
- try {
- inputText = input.readUTF();
- if (!inputText.equals("")) {
- //Check if the user has entered a command, and if so, handle it appropriately
- if (checkForCommand(commands, inputText).equals("")) {
- this.masterOut.forwardTextToOutputThreads(this.userName + ":" + inputText);
- } else if (checkForCommand(commands, inputText).equals("/help")) {
- this.correspondingThread.sendText("Here are the available commands:");
- Iterator<String> it = commands.iterator();
- while (it.hasNext()) {
- String s = it.next();
- if (!s.equals("/users")) {
- this.correspondingThread.sendText(s + descriptions.get(s));
- }
- }
- } else if (checkForCommand(commands, inputText).equals("/users")) {
- this.correspondingThread.sendText("Here are the connected users:");
- Iterator<InputThread> it = this.masterIn.threads().iterator();
- while (it.hasNext()) {
- this.correspondingThread.sendText(it.next().userName());
- }
- this.correspondingThread.sendText(":end");
- } else if (checkForCommand(commands, inputText).equals("/msg")) {
- String[] arguments = splitPrivateMessage(inputText);
- if (sendMessageIfUserOnline(arguments[1], "Private message from " + this.userName + ": " + arguments[2])) {
- this.correspondingThread.sendText("Private message sent to " + arguments[1] + ": " + arguments[2]);
- } else {
- this.correspondingThread.sendText("User " + arguments[1] + " is not online.");
- }
- }
- }
- //} catch (EOFException e) {
- //ignore end of file exceptions
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- }
- //If we have exited this loop, then the user has logged out. Send the logout code so the client exits properly
- this.correspondingThread.sendText("/logout");
- //Close connection.
- if (!this.socket.isClosed()) {
- try {
- input.close();
- this.socket.close();
- this.correspondingThread.kill();
- this.masterOut.removeThreadFromSet(this.correspondingThread);
- this.masterOut.forwardTextToOutputThreads("User " + this.userName + " has logged out.");
- this.masterIn.removeThreadFromSet(this);
- System.out.println("Closing input thread.");
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- }
- }
- void start() {
- if(this.t == null) {
- t = new Thread(this);
- t.start();
- }
- }
- String userName() {
- return this.userName;
- }
- OutputThread correspondingThread() {
- return this.correspondingThread;
- }
- }
- //Main class acts as a connection hub. Waits for connections, and upon receiving them, creates threads to handle each connection.
- public class Server {
- public static void main(String[] args) {
- //Set up connection variables
- int portNumber = Integer.parseInt(args[0]);
- Socket s = null;
- ServerSocket server = null;
- //Instantiate server socket
- System.out.println("Starting server.");
- try {
- server = new ServerSocket(portNumber);
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- //Setup the master output and input threads
- MasterOutputThread masterOut = new MasterOutputThread();
- MasterInputThread masterIn = new MasterInputThread();
- masterOut.start();
- masterIn.start();
- //Wait for connections
- while(true) {
- System.out.println("Waiting for client connection:");
- try {
- s = server.accept();
- } catch (IOException e) {
- e.printStackTrace();
- exit(1);
- }
- System.out.println("Connection established. Generating new thread to handle connection.");
- OutputThread out = new OutputThread(s);
- InputThread in = new InputThread(s, out, masterOut, masterIn);
- in.start();
- out.start();
- masterIn.addThreadToSet(in);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement