Skip to content
This repository has been archived by the owner on Nov 5, 2022. It is now read-only.

Commit

Permalink
implement basic chat functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Renari committed Apr 15, 2018
1 parent 3d7a5bc commit a4464f3
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 18 deletions.
33 changes: 31 additions & 2 deletions src/com/arimil/chataraxia/Main.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
package com.arimil.chataraxia;

import com.arimil.chataraxia.client.Client;
import com.arimil.chataraxia.server.Server;
import javafx.application.Application;

import java.util.Scanner;

public class Main {

public static boolean isServerSide = false;

public static void main(String[] args) {
if(args.length > 0 && args[0].equals("--server")) {
//start server on args[1] port
if(args.length >= 2 && args[0].equals("--server")) {
isServerSide = true;
int port;
try {
port = Integer.parseInt(args[1]);
} catch(NumberFormatException e) {
System.out.println("Unable to parse " + args[1] + " as integer, the format is --server <port>");
return;
}
System.out.println("Starting server on port: " + args[1]);
Server server = new Server(port);
new Thread(server).start();
Scanner s = new Scanner(System.in);
while(!server.isStopped()) {
String input = s.nextLine();
switch (input) {
case "shutdown" :
server.stop();
break;
case "connected" :
System.out.println("Connections: " + Server.clients.size());
break;
default:
System.out.println("Unknown command: " + input);
}
}
} else {
Application.launch(Client.class, args);
}
Expand Down
16 changes: 16 additions & 0 deletions src/com/arimil/chataraxia/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.arimil.chataraxia;

import java.io.Serializable;
import java.net.Socket;

public abstract class Message implements Serializable {
public void process(Socket sender) {
if(Main.isServerSide) {
server(sender);
} else {
client(sender);
}
}
protected abstract void client(Socket sender);
protected abstract void server(Socket sender);
}
20 changes: 19 additions & 1 deletion src/com/arimil/chataraxia/client/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,32 @@
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.util.List;

public class Client extends Application {

public static Controller contoller;

@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("client.fxml"));
FXMLLoader loader = new FXMLLoader(getClass().getResource("client.fxml"));
Parent root = loader.load();
contoller = loader.getController();
primaryStage.setTitle("Chataraxia");
primaryStage.setScene(new Scene(root));
primaryStage.show();

List<String> args = getParameters().getRaw();
if(args.size() >= 2) {
try {
String ip = args.get(0);
int port = Integer.parseInt(args.get(1));
((Controller) loader.getController()).connect(ip, port);
} catch (NumberFormatException e) {
System.out.println("Invalid port passed for automatic connection.");
System.out.println("Unable to connect to server with specified arguments.");
}
}
}

}
55 changes: 55 additions & 0 deletions src/com/arimil/chataraxia/client/ClientConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.arimil.chataraxia.client;

import com.arimil.chataraxia.Message;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

public class ClientConnection implements Runnable {

private String host;
private int port;
Socket socket = new Socket();
private ObjectOutputStream outputStream;

ClientConnection(String host, int port) {
this.host = host;
this.port = port;
}

public void send(Object obj) {
try {
if(outputStream != null) {
outputStream.writeObject(obj);
outputStream.reset();
} else {
Client.contoller.addMessage("Connect before trying to send messages '/connect <host> <port>'");
}
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void run() {
try {
socket.connect(new InetSocketAddress(host, port));
Client.contoller.addMessage("Connected to " + host + " on " + port);
outputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
while (socket.isConnected()) {
Object obj = inputStream.readObject();
Message msg = (Message)obj;
msg.process(socket);
}
} catch (IOException e) {
Client.contoller.addMessage("Unable to connect to " + host + " on " + port);
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
58 changes: 43 additions & 15 deletions src/com/arimil/chataraxia/client/Controller.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.arimil.chataraxia.client;

import com.arimil.chataraxia.messages.ChatMessage;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
Expand All @@ -13,6 +15,13 @@ public class Controller {
public TextField textField;
public WebView webView;
private WebEngine engine;
private ClientConnection connection;

@FXML
public void initialize() {
engine = webView.getEngine();
engine.loadContent("<!DOCTYPE html><html><head><meta charset=utf-8><title>ChatView</title><style>body{background-color:#585c5f;color:#fff}.sender{color:#0ff;font-weight:700}</style></head><body><div id=messageArea></div><script>function isScrollbarVisible(){return document.body.clientHeight>window.innerHeight}function isAtBottomOfPage(){return!isScrollbarVisible()||window.innerHeight+window.scrollY>=document.body.offsetHeight}function addMessage(e,n){var i=document.createElement(\"div\");i.innerHTML=void 0!==n?'<span class=\"sender\">'+n+\":</span> \"+e:e;var o=isAtBottomOfPage();document.getElementById(\"messageArea\").appendChild(i),o&&window.scrollTo(0,document.body.scrollHeight)}</script></body></html>");
}

private String stringToHtmlString(String s){
StringBuilder sb = new StringBuilder();
Expand All @@ -24,40 +33,59 @@ private String stringToHtmlString(String s){
case '>': sb.append("&gt;"); break;
case '&': sb.append("&amp;"); break;
case '"': sb.append("&quot;"); break;
case '\'': sb.append("\\'"); break;
default: sb.append(c); break;
}
}
return sb.toString();
}

@FXML
public void initialize() {
engine = webView.getEngine();
engine.loadContent("<!DOCTYPE html><html><head><meta charset=utf-8><title>ChatView</title><style>body{background-color:#585c5f;color:#fff}.sender{color:#0ff;font-weight:700}</style></head><body><div id=messageArea></div><script>function isScrollbarVisible(){return document.body.clientHeight>window.innerHeight}function isAtBottomOfPage(){return!isScrollbarVisible()||window.innerHeight+window.scrollY>=document.body.offsetHeight}function addMessage(e,n){var i=document.createElement(\"div\");i.innerHTML=void 0!==n?'<span class=\"sender\">'+n+\":</span> \"+e:e;var o=isAtBottomOfPage();document.getElementById(\"messageArea\").appendChild(i),o&&window.scrollTo(0,document.body.scrollHeight)}</script></body></html>");
public void connect(String ip, int port) {
if(connection == null || !connection.socket.isConnected()) {
connection = new ClientConnection(ip, port);
new Thread(connection).start();
}
}

public void addMessage(String message) {
addMessage(message, null);
}

public void addMessage(String message, String from) {
WebEngine engine = webView.getEngine();

if(from != null) {
engine.executeScript("addMessage('" + stringToHtmlString(message) + "', '" + stringToHtmlString(from) + "')");
} else {
engine.executeScript("addMessage('" + stringToHtmlString(message) + "')");
}
Platform.runLater(() -> {
if(from != null) {
engine.executeScript("addMessage('" + stringToHtmlString( message) + "', '" + stringToHtmlString(from) + "')");
} else {
engine.executeScript("addMessage('" + stringToHtmlString(message) + "')");
}
});
}

@FXML
private void keyListener(KeyEvent event) {
if (event.getCode() == KeyCode.ENTER) {
if (textField.isFocused()) {
addMessage(textField.getText(), "test");
addMessage("this is a system message");
textField.setText(""); // clear text after message is sent

if(textField.getText(0, 1).equals("/")) {
String input = textField.getText();
String[] command = input.split(" ");
switch (command[0]) {
case "/connect":
if(command.length >= 3) {
try {
int port = Integer.parseInt(command[2]);
connect(command[1], port);
} catch (NumberFormatException e) {
addMessage("Invalid port specified");
}
} else {
addMessage("/connect <host> <port>");
}
break;
}
} else {
connection.send(new ChatMessage(textField.getText()));
}
textField.setText(""); // clear text
// remove focus from text field
Scene scene = textField.getScene();
scene.getRoot().requestFocus();
Expand Down
43 changes: 43 additions & 0 deletions src/com/arimil/chataraxia/messages/ChatMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.arimil.chataraxia.messages;

import com.arimil.chataraxia.Message;
import com.arimil.chataraxia.client.Client;
import com.arimil.chataraxia.server.ClientData;
import com.arimil.chataraxia.server.Server;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Map;

public class ChatMessage extends Message {

private String from;
private String message;

public ChatMessage(String message) {
this.message = message;
}

@Override
protected void client(Socket sender) {
if(from != null) {
Client.contoller.addMessage(from, message);
} else {
Client.contoller.addMessage(message);
}
}

@Override
protected void server(Socket sender) {
this.from = Server.clients.get(sender).getName();
for (Map.Entry<Socket, ClientData> entry : Server.clients.entrySet())
try {
ObjectOutputStream outputStream = entry.getValue().getOutputStream();
outputStream.writeObject(this);
outputStream.reset();
} catch (IOException e) {
e.printStackTrace();
}
}
}
54 changes: 54 additions & 0 deletions src/com/arimil/chataraxia/messages/LoginMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.arimil.chataraxia.messages;

import com.arimil.chataraxia.Message;
import com.arimil.chataraxia.server.Server;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class LoginMessage extends Message {

String name;
String pass;

public LoginMessage(String name, String pass) {
this.name = name;
this.pass = pass;
}

@Override
protected void client(Socket sender) {

}

@Override
protected void server(Socket sender) {
File file = FileSystems.getDefault().getPath("users\\"+name).toFile();
try {
ObjectOutputStream outputStream = Server.clients.get(sender).getOutputStream();
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(pass.getBytes(StandardCharsets.UTF_8));
String passwordHash = new String(hash, StandardCharsets.UTF_8);
if(file.exists()) {
BufferedReader br = new BufferedReader(new FileReader(file));
if(br.readLine().equals(passwordHash)) {
Server.clients.get(sender).setAuth(true);
} else {
outputStream.writeObject(new ChatMessage("Invalid username or password"));
}
} else {
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
bw.write(passwordHash);
Server.clients.get(sender).setAuth(true);
outputStream.writeObject(new ChatMessage("Logged in successfully"));
}
outputStream.reset();
} catch (IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
36 changes: 36 additions & 0 deletions src/com/arimil/chataraxia/server/ClientData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.arimil.chataraxia.server;

import java.io.ObjectOutputStream;

public class ClientData {

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

private String name;

public boolean isAuth() {
return auth;
}

public void setAuth(boolean auth) {
this.auth = auth;
}

private boolean auth = false;

public ObjectOutputStream getOutputStream() {
return outputStream;
}

public void setOutputStream(ObjectOutputStream outputStream) {
this.outputStream = outputStream;
}

private ObjectOutputStream outputStream;
}
Loading

0 comments on commit a4464f3

Please sign in to comment.