Advertisement
Guest User

Untitled

a guest
Mar 1st, 2018
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.45 KB | None | 0 0
  1. import java.io.*;
  2. import java.net.ServerSocket;
  3. import java.net.Socket;
  4. import java.util.*;
  5.  
  6. import static java.lang.System.exit;
  7.  
  8. //Handles all other output threads -- tells them what to say
  9. class MasterOutputThread implements Runnable {
  10. private Set<OutputThread> outThreads;
  11. private Thread t;
  12.  
  13. MasterOutputThread() {
  14. this.outThreads = new HashSet<>();
  15. }
  16.  
  17. public void run() {
  18. Scanner console = new Scanner(System.in);
  19.  
  20. while(true) {
  21. String text = console.nextLine();
  22. System.out.println("Received text " + text + " in master.");
  23. Iterator<OutputThread> it = this.outThreads.iterator();
  24. while (it.hasNext()) {
  25. it.next().sendText("Server announcement: " + text);
  26. }
  27. }
  28. }
  29.  
  30. void start() {
  31. if(this.t == null) {
  32. t = new Thread(this);
  33. t.start();
  34. }
  35. }
  36.  
  37. void addThreadToSet(OutputThread thread) {
  38. this.outThreads.add(thread);
  39. }
  40.  
  41. void removeThreadFromSet(OutputThread thread) {
  42. this.outThreads.remove(thread);
  43. }
  44.  
  45. public Set<OutputThread> threads() {
  46. return this.outThreads;
  47. }
  48.  
  49. void forwardTextToOutputThreads(String text){
  50. Iterator<OutputThread> it = this.outThreads.iterator();
  51. while (it.hasNext()) {
  52. it.next().sendText(text);
  53. }
  54. }
  55. }
  56.  
  57. //Thread class to handle output -- sending data to client
  58. class OutputThread implements Runnable {
  59. //Instance variables
  60. private Socket socket;
  61. private Thread t;
  62. private DataOutputStream output;
  63. private String userName;
  64.  
  65. //Constructor
  66. OutputThread(Socket s) {
  67. this.socket = s;
  68. this.output = null;
  69.  
  70. try {
  71. output = new DataOutputStream(this.socket.getOutputStream());
  72. } catch (IOException e) {
  73. e.printStackTrace();
  74. exit(1);
  75. }
  76. }
  77.  
  78. public void run() {
  79.  
  80. //Program loop -- doesn't actually do anything. Just keeps the thread running so that the sendText method can be called.
  81. while(true) {
  82. }
  83. }
  84.  
  85. void start() {
  86. if(this.t == null) {
  87. t = new Thread(this);
  88. t.start();
  89. }
  90. }
  91.  
  92. //Send text to the client.
  93. void sendText(String text) {
  94. System.out.println("Sending text " + text + " to " + this.userName + ".");
  95. try {
  96. this.output.writeUTF(text);
  97. } catch (IOException e) {
  98. e.printStackTrace();
  99. exit(1);
  100. }
  101. }
  102.  
  103. void kill() {
  104. t.interrupt();
  105. try {
  106. this.socket.close();
  107. } catch (IOException e) {
  108. e.printStackTrace();
  109. exit(1);
  110. }
  111. }
  112.  
  113. void setUserName(String user) {
  114. this.userName = user;
  115. }
  116. }
  117.  
  118. //Collection of all input threads
  119. class MasterInputThread implements Runnable {
  120.  
  121. class User {
  122. private String userName;
  123. private String password;
  124.  
  125. User(String user, String pass){
  126. this.userName = user;
  127. this.password = pass;
  128. }
  129.  
  130. String userName(){
  131. return this.userName;
  132. }
  133.  
  134. String password(){
  135. return this.password;
  136. }
  137. }
  138.  
  139. //Instance variables
  140. private Thread t;
  141. private Set<InputThread> inThreads;
  142. private Set<User> users;
  143.  
  144. //Helper method to split a line from the file into a user object. Assumes the line has a colon separating the username and password.
  145. private User convertTextToUser(String line){
  146. int i = 0;
  147. while (i < line.length() && line.charAt(i) != ':') {
  148. i++;
  149. }
  150. String user = line.substring(0, i);
  151. String pass = line.substring(i+1);
  152. return new User(user, pass);
  153. }
  154.  
  155. private Set<User> loadUsersFromFile(String filename){
  156. Set<User> result = new HashSet<>();
  157.  
  158. //Set up the file reader
  159. BufferedReader fileIn = null;
  160. boolean fileExists = true;
  161. try {
  162. fileIn = new BufferedReader(new FileReader(filename));
  163. } catch (FileNotFoundException e) {
  164. //Mark that file doesn't exist. If there is no file then we just want the set of users to be an empty set.
  165. fileExists = false;
  166. }
  167.  
  168. //Load the text into user objects.
  169. if (fileExists) {
  170. try {
  171. String line = fileIn.readLine();
  172. while (line != null) {
  173. result.add(convertTextToUser(line));
  174. line = fileIn.readLine();
  175. }
  176. } catch (IOException e) {
  177. e.printStackTrace();
  178. exit(1);
  179. }
  180. }
  181. return result;
  182. }
  183.  
  184. private void saveUsersToFile(String filename){
  185. //Set up the file output
  186. PrintWriter fileOut = null;
  187. try {
  188. fileOut = new PrintWriter(filename, "UTF-8");
  189. } catch (IOException e){
  190. e.printStackTrace();
  191. exit(1);
  192. }
  193. //Get the list of users and print each to the file. Each will be on its own line, with a colon separating
  194. //the username and password
  195. Iterator<User> it = this.users.iterator();
  196. while(it.hasNext()){
  197. User u = it.next();
  198. fileOut.println(u.userName() + ":" + u.password());
  199. }
  200. //Close the file
  201. fileOut.close();
  202. }
  203.  
  204. MasterInputThread(){
  205. this.inThreads = new HashSet<>();
  206. this.users = loadUsersFromFile(System.getProperty("user.dir") + "/users.txt");
  207. }
  208.  
  209. public void run(){
  210.  
  211. //Program loop -- doesn't actually do anything other than keep the thread running.
  212. while(true){
  213.  
  214. }
  215. }
  216.  
  217. void start(){
  218. if(this.t == null) {
  219. t = new Thread(this);
  220. t.start();
  221. }
  222. }
  223.  
  224. void addThreadToSet(InputThread thread) {
  225. this.inThreads.add(thread);
  226. }
  227.  
  228. void removeThreadFromSet(InputThread thread) {
  229. this.inThreads.remove(thread);
  230. }
  231.  
  232. public Set<InputThread> threads() {
  233. return this.inThreads;
  234. }
  235.  
  236. void registerUser(String user, String pass){
  237. this.users.add(new User(user, pass));
  238. this.saveUsersToFile(System.getProperty("user.dir") + "/users.txt");
  239. }
  240.  
  241. //Method to check if the specified username already exists
  242. boolean checkIfUserExists(String user){
  243. Iterator<User> it = this.users.iterator();
  244. while(it.hasNext()){
  245. User u = it.next();
  246. if (u.userName().equals(user)){
  247. return true;
  248. }
  249. }
  250. return false;
  251. }
  252.  
  253. //Method to check if a given password matches the one on file for a user
  254. boolean checkPassword(String user, String pass) {
  255. Iterator<User> it = this.users.iterator();
  256. User desiredUser = null;
  257. while (it.hasNext() && desiredUser == null){
  258. User u = it.next();
  259. if (u.userName().equals(user)){
  260. desiredUser = u;
  261. }
  262. }
  263. return desiredUser.password().equals(pass);
  264. }
  265. }
  266.  
  267. //Thread class to handle input -- receiving data from client
  268. class InputThread implements Runnable {
  269. //Instance variables
  270. private Socket socket;
  271. private Thread t;
  272. private OutputThread correspondingThread;
  273. private MasterOutputThread masterOut;
  274. private MasterInputThread masterIn;
  275. private String userName;
  276.  
  277. //Constructor.
  278. InputThread(Socket s, OutputThread thread, MasterOutputThread masterOutThread, MasterInputThread masterInThread) {
  279. this.socket = s;
  280. this.correspondingThread = thread;
  281. this.masterOut = masterOutThread;
  282. this.masterIn = masterInThread;
  283. }
  284.  
  285. //Create a list of strings that the server will treat as user commands. This is done as a set rather than a static
  286. //collection to support expansion with more commands.
  287. private Set<String> createSetOfCommands() {
  288. Set<String> result = new HashSet<>();
  289. result.add("/users");
  290. result.add("/logout");
  291. result.add("/help");
  292. result.add("/msg");
  293.  
  294. return result;
  295. }
  296.  
  297. //Create a map that has definitions of what each command does.
  298. private Map<String, String> createMapOfDescriptions() {
  299. Map<String, String> result = new HashMap<>();
  300. result.put("/users", " - Gives a list of currently logged in users");
  301. result.put("/logout", " - Log out of the chat");
  302. result.put("/help", " - Shows this list of commands");
  303. result.put("/msg", " <user> <message> - Sends a private message to a specified user");
  304.  
  305. return result;
  306. }
  307.  
  308. private String checkForCommand(Set<String> commands, String text) {
  309. Iterator<String> it = commands.iterator();
  310. while (it.hasNext()) {
  311. String command = it.next();
  312. if (text.startsWith(command)) {
  313. return command;
  314. }
  315. }
  316. return "";
  317. }
  318.  
  319. private String[] splitPrivateMessage(String text) {
  320. String[] result = new String[3];
  321. int i = 4, j = 5;
  322.  
  323. //Store the /msg part
  324. result[0] = text.substring(0, i);
  325.  
  326. //Store the user name
  327. while (j < text.length() && text.charAt(j) != ' ') {
  328. j++;
  329. }
  330. result[1] = text.substring(i+1, j);
  331.  
  332. //Store the message
  333. result[2] = text.substring(j+1);
  334.  
  335. return result;
  336. }
  337.  
  338. private boolean sendMessageIfUserOnline(String name, String message) {
  339. Iterator<InputThread> it = this.masterIn.threads().iterator();
  340. while (it.hasNext()) {
  341. InputThread user = it.next();
  342. if (name.equals(user.userName())) {
  343. user.correspondingThread().sendText(message);
  344. return true;
  345. }
  346. }
  347. return false;
  348. }
  349.  
  350. public void run() {
  351. //Set up connection variables
  352. DataInputStream input = null;
  353. try {
  354. input = new DataInputStream(new BufferedInputStream(this.socket.getInputStream()));
  355. } catch (IOException e) {
  356. e.printStackTrace();
  357. exit(1);
  358. }
  359.  
  360. //Determine whether the user is a new user or not
  361. String inputText = "a";
  362. while (!inputText.equals("1") && !inputText.equals("2")){
  363. try {
  364. inputText = input.readUTF();
  365. } catch (EOFException e) {
  366. //Do nothing
  367. } catch(IOException e){
  368. e.printStackTrace();
  369. exit(1);
  370. }
  371. if (!inputText.equals("1") && !inputText.equals("2")){
  372. this.correspondingThread.sendText("Input not recognized.");
  373. }
  374. }
  375. //String inputText will be "2" if new user, else they will be an existing user
  376. boolean newUser = inputText.equals("2");
  377.  
  378. boolean authenticationComplete = false;
  379. while (!authenticationComplete) {
  380. //Get the username
  381. inputText = "a";
  382. if (newUser) {
  383. boolean acceptableUsername = false;
  384. if (!acceptableUsername || inputText.contains(":")) {
  385. try {
  386. inputText = input.readUTF();
  387. } catch (IOException e) {
  388. e.printStackTrace();
  389. exit(1);
  390. }
  391.  
  392. //Tell the user if there is anything wrong with their new username
  393. if (inputText.contains(":")) {
  394. this.correspondingThread.sendText("Usernames cannot contain ':'. Please try again.");
  395. continue;
  396. } else if (this.masterIn.checkIfUserExists(inputText)) {
  397. this.correspondingThread.sendText("Username already exists. Please try again.");
  398. continue;
  399. } else {
  400. acceptableUsername = true;
  401. }
  402. }
  403. } else {
  404. boolean nameExists = false;
  405. if (!nameExists) {
  406. try {
  407. inputText = input.readUTF();
  408. } catch (IOException e) {
  409. e.printStackTrace();
  410. exit(1);
  411. }
  412. if (this.masterIn.checkIfUserExists(inputText)) {
  413. nameExists = true;
  414. } else {
  415. this.correspondingThread.sendText("Username does not exist. Try again.");
  416. continue;
  417. }
  418. }
  419. }
  420. this.userName = inputText;
  421. this.correspondingThread.setUserName(inputText);
  422.  
  423. //Get the password
  424. this.correspondingThread.sendText("Enter password:");
  425. try {
  426. inputText = input.readUTF();
  427. } catch (IOException e) {
  428. e.printStackTrace();
  429. exit(1);
  430. }
  431.  
  432. //Check if the user entered "" as a password
  433. if (inputText.equals("")) {
  434. this.correspondingThread.sendText("Password must be greater than 0 characters.");
  435. continue;
  436. }
  437.  
  438. //Confirm the password if new user, else check if the password matches the one on file
  439. if (newUser) {
  440. String confirm = null;
  441. if (confirm == null || !inputText.equals(confirm)) {
  442. this.correspondingThread.sendText("Confirm password:");
  443. System.out.println(inputText);
  444. try {
  445. confirm = input.readUTF();
  446. } catch (IOException e) {
  447. e.printStackTrace();
  448. exit(1);
  449. }
  450. System.out.println(confirm);
  451. if (!inputText.equals(confirm)) {
  452. this.correspondingThread.sendText("Passwords do not match. Try again.");
  453. continue;
  454. } else {
  455. this.correspondingThread.sendText("Welcome to the chat! Type /help for a list of commands.");
  456. authenticationComplete = true;
  457. }
  458. }
  459. this.masterIn.registerUser(this.userName, inputText);
  460. } else {
  461. if (masterIn.checkPassword(this.userName, inputText)) {
  462. this.correspondingThread.sendText("Welcome to the chat! Type /help for a list of commands.");
  463. authenticationComplete = true;
  464. } else {
  465. this.correspondingThread.sendText("Authentication failed.");
  466. continue;
  467. }
  468. }
  469. }
  470. //Alert other users that someone has logged on.
  471. this.masterOut.forwardTextToOutputThreads("User " + this.userName + " has logged on.");
  472.  
  473. //Add the corresponding output thread to the list of threads that get messages delivered.
  474. this.masterOut.addThreadToSet(this.correspondingThread);
  475.  
  476. //Get the list of valid commands and their descriptions.
  477. Set<String> commands = createSetOfCommands();
  478. Map<String, String> descriptions = createMapOfDescriptions();
  479.  
  480. //Program loop. Echo text to the server. Forward the text to the master output thread.
  481. inputText = "a";
  482. while (!checkForCommand(commands, inputText).equals("/logout")) {
  483. try {
  484. inputText = input.readUTF();
  485. if (!inputText.equals("")) {
  486. //Check if the user has entered a command, and if so, handle it appropriately
  487. if (checkForCommand(commands, inputText).equals("")) {
  488. this.masterOut.forwardTextToOutputThreads(this.userName + ":" + inputText);
  489. } else if (checkForCommand(commands, inputText).equals("/help")) {
  490. this.correspondingThread.sendText("Here are the available commands:");
  491. Iterator<String> it = commands.iterator();
  492. while (it.hasNext()) {
  493. String s = it.next();
  494. if (!s.equals("/users")) {
  495. this.correspondingThread.sendText(s + descriptions.get(s));
  496. }
  497. }
  498. } else if (checkForCommand(commands, inputText).equals("/users")) {
  499. this.correspondingThread.sendText("Here are the connected users:");
  500. Iterator<InputThread> it = this.masterIn.threads().iterator();
  501. while (it.hasNext()) {
  502. this.correspondingThread.sendText(it.next().userName());
  503. }
  504. this.correspondingThread.sendText(":end");
  505. } else if (checkForCommand(commands, inputText).equals("/msg")) {
  506. String[] arguments = splitPrivateMessage(inputText);
  507. if (sendMessageIfUserOnline(arguments[1], "Private message from " + this.userName + ": " + arguments[2])) {
  508. this.correspondingThread.sendText("Private message sent to " + arguments[1] + ": " + arguments[2]);
  509. } else {
  510. this.correspondingThread.sendText("User " + arguments[1] + " is not online.");
  511. }
  512. }
  513. }
  514. //} catch (EOFException e) {
  515. //ignore end of file exceptions
  516. } catch (IOException e) {
  517. e.printStackTrace();
  518. exit(1);
  519. }
  520. }
  521. //If we have exited this loop, then the user has logged out. Send the logout code so the client exits properly
  522. this.correspondingThread.sendText("/logout");
  523.  
  524. //Close connection.
  525. if (!this.socket.isClosed()) {
  526. try {
  527. input.close();
  528. this.socket.close();
  529. this.correspondingThread.kill();
  530. this.masterOut.removeThreadFromSet(this.correspondingThread);
  531. this.masterOut.forwardTextToOutputThreads("User " + this.userName + " has logged out.");
  532. this.masterIn.removeThreadFromSet(this);
  533. System.out.println("Closing input thread.");
  534. } catch (IOException e) {
  535. e.printStackTrace();
  536. exit(1);
  537. }
  538. }
  539. }
  540.  
  541. void start() {
  542. if(this.t == null) {
  543. t = new Thread(this);
  544. t.start();
  545. }
  546. }
  547.  
  548. String userName() {
  549. return this.userName;
  550. }
  551.  
  552. OutputThread correspondingThread() {
  553. return this.correspondingThread;
  554. }
  555. }
  556.  
  557. //Main class acts as a connection hub. Waits for connections, and upon receiving them, creates threads to handle each connection.
  558. public class Server {
  559.  
  560. public static void main(String[] args) {
  561. //Set up connection variables
  562. int portNumber = Integer.parseInt(args[0]);
  563. Socket s = null;
  564. ServerSocket server = null;
  565.  
  566. //Instantiate server socket
  567. System.out.println("Starting server.");
  568. try {
  569. server = new ServerSocket(portNumber);
  570. } catch (IOException e) {
  571. e.printStackTrace();
  572. exit(1);
  573. }
  574.  
  575. //Setup the master output and input threads
  576. MasterOutputThread masterOut = new MasterOutputThread();
  577. MasterInputThread masterIn = new MasterInputThread();
  578. masterOut.start();
  579. masterIn.start();
  580.  
  581. //Wait for connections
  582. while(true) {
  583. System.out.println("Waiting for client connection:");
  584. try {
  585. s = server.accept();
  586. } catch (IOException e) {
  587. e.printStackTrace();
  588. exit(1);
  589. }
  590. System.out.println("Connection established. Generating new thread to handle connection.");
  591. OutputThread out = new OutputThread(s);
  592. InputThread in = new InputThread(s, out, masterOut, masterIn);
  593. in.start();
  594. out.start();
  595. masterIn.addThreadToSet(in);
  596. }
  597. }
  598. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement