├── Client.java ├── README.md └── Server.java /Client.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedInputStream; 2 | import java.io.BufferedOutputStream; 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileOutputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.ObjectInputStream; 9 | import java.io.ObjectOutputStream; 10 | import java.io.IOException; 11 | import java.net.Socket; 12 | import java.net.UnknownHostException; 13 | 14 | public class Client implements Runnable { 15 | 16 | private static Socket clientSocket = null; 17 | private static ObjectOutputStream os = null; 18 | private static ObjectInputStream is = null; 19 | private static BufferedReader inputLine = null; 20 | private static BufferedInputStream bis = null; 21 | private static boolean closed = false; 22 | 23 | public static void main(String[] args) { 24 | 25 | // The default port. 26 | int portNumber = 1234; 27 | // The default host. 28 | String host = "localhost"; 29 | 30 | if (args.length < 2) { 31 | System.out.println("Default Server: " + host + ", Default Port: " + portNumber); 32 | } else { 33 | host = args[0]; 34 | portNumber = Integer.valueOf(args[1]).intValue(); 35 | System.out.println("Server: " + host + ", Port: " + portNumber); 36 | } 37 | 38 | /* 39 | * Open a socket on a given host and port. Open input and output streams. 40 | */ 41 | try { 42 | clientSocket = new Socket(host, portNumber); 43 | inputLine = new BufferedReader(new InputStreamReader(System.in)); 44 | os = new ObjectOutputStream(clientSocket.getOutputStream()); 45 | is = new ObjectInputStream(clientSocket.getInputStream()); 46 | } catch (UnknownHostException e) { 47 | System.err.println("Unknown " + host); 48 | } catch (IOException e) { 49 | System.err.println("No Server found. Please ensure that the Server program is running and try again."); 50 | } 51 | 52 | /* 53 | * If everything has been initialized then we want to write some data to the 54 | * socket we have opened a connection to on the port portNumber. 55 | */ 56 | if (clientSocket != null && os != null && is != null) { 57 | try { 58 | 59 | /* Create a thread to read from the server. */ 60 | new Thread(new Client()).start(); 61 | while (!closed) { 62 | 63 | /* Read input from Client */ 64 | 65 | String msg = (String) inputLine.readLine().trim(); 66 | 67 | /* Check the input for private messages or files */ 68 | 69 | if ((msg.split(":").length > 1)) 70 | { 71 | if (msg.split(":")[1].toLowerCase().startsWith("sendfile")) 72 | { 73 | File sfile = new File((msg.split(":")[1]).split(" ",2)[1]); 74 | 75 | if (!sfile.exists()) 76 | { 77 | System.out.println("File Doesn't exist!!"); 78 | continue; 79 | } 80 | 81 | byte [] mybytearray = new byte [(int)sfile.length()]; 82 | FileInputStream fis = new FileInputStream(sfile); 83 | bis = new BufferedInputStream(fis); 84 | while (bis.read(mybytearray,0,mybytearray.length)>=0) 85 | { 86 | bis.read(mybytearray,0,mybytearray.length); 87 | } 88 | os.writeObject(msg); 89 | os.writeObject(mybytearray); 90 | os.flush(); 91 | 92 | } 93 | else 94 | { 95 | os.writeObject(msg); 96 | os.flush(); 97 | } 98 | 99 | } 100 | 101 | /* Check the input for broadcast files */ 102 | 103 | else if (msg.toLowerCase().startsWith("sendfile")) 104 | { 105 | 106 | File sfile = new File(msg.split(" ",2)[1]); 107 | 108 | if (!sfile.exists()) 109 | { 110 | System.out.println("File Doesn't exist!!"); 111 | continue; 112 | } 113 | 114 | byte [] mybytearray = new byte [(int)sfile.length()]; 115 | FileInputStream fis = new FileInputStream(sfile); 116 | bis = new BufferedInputStream(fis); 117 | while (bis.read(mybytearray,0,mybytearray.length)>=0) 118 | { 119 | bis.read(mybytearray,0,mybytearray.length); 120 | } 121 | os.writeObject(msg); 122 | os.writeObject(mybytearray); 123 | os.flush(); 124 | 125 | } 126 | 127 | /* Check the input for broadcast messages */ 128 | 129 | else 130 | { 131 | os.writeObject(msg); 132 | os.flush(); 133 | } 134 | 135 | 136 | } 137 | 138 | /* 139 | * Close all the open streams and socket. 140 | */ 141 | os.close(); 142 | is.close(); 143 | clientSocket.close(); 144 | } catch (IOException e) 145 | { 146 | System.err.println("IOException: " + e); 147 | } 148 | 149 | 150 | } 151 | } 152 | 153 | /* 154 | * Create a thread to read from the server. 155 | */ 156 | 157 | public void run() { 158 | /* 159 | * Keep on reading from the socket till we receive "Bye" from the 160 | * server. Once we received that then we want to break. 161 | */ 162 | String responseLine; 163 | String filename = null; 164 | byte[] ipfile = null; 165 | FileOutputStream fos = null; 166 | BufferedOutputStream bos = null; 167 | File directory_name = null; 168 | String full_path; 169 | String dir_name = "Received_Files"; 170 | 171 | try { 172 | 173 | 174 | while ((responseLine = (String) is.readObject()) != null) { 175 | 176 | /* Condition for Directory Creation */ 177 | 178 | if (responseLine.equals("Directory Created")) 179 | { 180 | //Creating Receiving Folder 181 | 182 | directory_name = new File((String) dir_name); 183 | 184 | if (!directory_name.exists()) 185 | { 186 | directory_name.mkdir(); 187 | 188 | System.out.println("New Receiving file directory for this client created!!"); 189 | 190 | } 191 | 192 | else 193 | { 194 | System.out.println("Receiving file directory for this client already exists!!"); 195 | } 196 | } 197 | 198 | /* Condition for Checking for incoming files */ 199 | 200 | else if (responseLine.startsWith("Sending_File")) 201 | { 202 | 203 | try 204 | { 205 | filename = responseLine.split(":")[1]; 206 | full_path = directory_name.getAbsolutePath()+"/"+filename; 207 | ipfile = (byte[]) is.readObject(); 208 | fos = new FileOutputStream(full_path); 209 | bos = new BufferedOutputStream(fos); 210 | bos.write(ipfile); 211 | bos.flush(); 212 | System.out.println("File Received."); 213 | } 214 | finally 215 | { 216 | if (fos != null) fos.close(); 217 | if (bos != null) bos.close(); 218 | } 219 | 220 | } 221 | 222 | /* Condition for Checking for incoming messages */ 223 | 224 | else 225 | { 226 | System.out.println(responseLine); 227 | } 228 | 229 | 230 | /* Condition for quitting application */ 231 | 232 | if (responseLine.indexOf("*** Bye") != -1) 233 | 234 | break; 235 | } 236 | 237 | closed = true; 238 | System.exit(0); 239 | 240 | } catch (IOException | ClassNotFoundException e) { 241 | 242 | System.err.println("Server Process Stopped Unexpectedly!!"); 243 | 244 | } 245 | } 246 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Single Server Multi-Client Chat Application including File Transfer 3 | 4 | This application consists of a server.java and a client.java files representing the client and server programs of the chat application. Server program uses TCP connection protocol to listen for clients connecting to its socket using the port specified by the user. If the user doesn't specify any port then the server runs on port 1234. 5 | 6 | ## Execution Steps for Server.java 7 | 8 | 1. Open Command Line and navigate to the folder where the file Server.java is present. 9 | 2. Type the command javac Server.java for compilation of the the Server. 10 | 3. Type the command java Server 1233 to execute the program where 1233 is the port. 11 | 4. The server displays a message "Server is running using specified port number=1233". 12 | This message proves that the server program is running and is waiting for clients 13 | to connect to this server. 14 | 5. If the user doesn’t specify any port, then the Server uses default port of 1234. 15 | 16 | ## Execution Steps for Client.java 17 | 18 | 1. Open Command Line and navigate to the folder where the file Client.java is present. 19 | 2. Type the command javac Client.java for compilation of the the Server. 20 | 3. Type the command java Client localhost 1233 to execute the program where 21 | localhost is the server name and 1233 is the port. 22 | 4. The client program prompts for the username. 23 | 5. Once the client enters the name and hits enter, the server displays a message 24 | "Client 1 is connected!" confirming that the Client1 is connected to the server. 25 | 6. Also, the client is prompted as 26 | ``` 27 | *** Welcome Client1 to our chat room *** 28 | Enter /quit to leave the chat room 29 | New Receiving file directory for this client created!! 30 | Please Enter command: 31 | ``` 32 | 7. Similarly, other clients can be connected to the server using the same Client.java 33 | program. Please note that if the testing of the program is done in a single machine, 34 | then there should be separate folders created for server and each clients. 35 | 8. If the user doesn’t specify any port, then the Client uses localhost and 36 | default port of 1234. 37 | 38 | 39 | ## Functionalities performed by this application 40 | 41 | ### 1. Broadcast : 42 | This feature enables a user to send a message or a file to all other clients connected to the server. For sending file a keyword sendfile is used followed by the filename. 43 | ``` 44 | Example for sending message: 45 | —————————————————————————————— 46 | Please Enter command: 47 | Hello 48 | 49 | Example for sending file: 50 | —————————————————————————————— 51 | Please Enter command: 52 | sendfile test.pdf 53 | 54 | ``` 55 | ### 2. Unicast : 56 | This feature enables a user to send a message or a file to a particular client connected to the server. 57 | ``` 58 | For sending message the following format should be followed:- 59 | —————————————————————————————————————————————————————————————— 60 | 61 | @: 62 | 63 | For sending file the following format should be followed:- 64 | —————————————————————————————————————————————————————————————— 65 | 66 | @:sendfile 67 | 68 | 69 | Example for sending message: 70 | —————————————————————————————— 71 | Please Enter command: 72 | @Client1:Hello 73 | 74 | Example for sending file: 75 | —————————————————————————————— 76 | Please Enter command: 77 | @Client1:sendfile test.pdf 78 | ``` 79 | ### 3. BlockCast : 80 | This feature enables a user to send a message or a file to a all clients except a particular client connected to the server. 81 | ``` 82 | For sending message the following format should be followed:- 83 | —————————————————————————————————————————————————————————————— 84 | 85 | !: 86 | 87 | For sending file the following format should be followed:- 88 | —————————————————————————————————————————————————————————————— 89 | 90 | !:sendfile 91 | 92 | 93 | Example for sending message: 94 | ———————————————————————————— 95 | Please Enter command: 96 | !Client1:Hello 97 | 98 | Example for sending file: 99 | ———————————————————————————— 100 | Please Enter command: 101 | !Client1:sendfile test.pdf 102 | ``` 103 | 104 | 105 | ## Important Notes:- 106 | The server program should be running before running any client program. The application has been tested for transferring any type of file upto a size of 250MB. Any file over this size may or may not support due to availability of java heap size. 107 | In case of issues, the heap size should be increased or file should be broken into smaller chunks in order to transfer bigger files. 108 | 109 | ## References:- 110 | 111 | 1. http://java.sun.com/docs/books/tutorial/networking/sockets/ 112 | 2. http://makemobiapps.blogspot.com/p/multiple-client-server-chat-programming.html 113 | 114 | ## Acknowledgements : 115 | Thank you very much to the professor and the TAs for their continued Support. 116 | -------------------------------------------------------------------------------- /Server.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.io.IOException; 3 | import java.io.ObjectInputStream; 4 | import java.io.ObjectOutputStream; 5 | import java.net.Socket; 6 | import java.util.ArrayList; 7 | 8 | import java.net.ServerSocket; 9 | 10 | /* 11 | * A chat server that delivers public and private messages and files. 12 | */ 13 | public class Server { 14 | 15 | // The server socket. 16 | private static ServerSocket serverSocket = null; 17 | // The client socket. 18 | private static Socket clientSocket = null; 19 | 20 | public static ArrayList clients = new ArrayList(); 21 | 22 | public static void main(String args[]) { 23 | 24 | // The default port number. 25 | int portNumber = 1234; 26 | 27 | 28 | if (args.length < 1) 29 | { 30 | 31 | System.out.println("No port specified by user.\nServer is running using default port number=" + portNumber); 32 | 33 | } 34 | else 35 | { 36 | portNumber = Integer.valueOf(args[0]).intValue(); 37 | 38 | System.out.println("Server is running using specified port number=" + portNumber); 39 | } 40 | 41 | /* 42 | * Open a server socket on the portNumber (default 1234). 43 | */ 44 | try { 45 | serverSocket = new ServerSocket(portNumber); 46 | } catch (IOException e) { 47 | System.out.println("Server Socket cannot be created"); 48 | } 49 | 50 | /* 51 | * Create a client socket for each connection and pass it to a new client 52 | * thread. 53 | */ 54 | 55 | int clientNum = 1; 56 | while (true) { 57 | try { 58 | 59 | clientSocket = serverSocket.accept(); 60 | clientThread curr_client = new clientThread(clientSocket, clients); 61 | clients.add(curr_client); 62 | curr_client.start(); 63 | System.out.println("Client " + clientNum + " is connected!"); 64 | clientNum++; 65 | 66 | } catch (IOException e) { 67 | 68 | System.out.println("Client could not be connected"); 69 | } 70 | 71 | 72 | } 73 | 74 | } 75 | } 76 | 77 | /* 78 | * This client thread class handles individual clients in their respective threads 79 | * by opening a separate input and output streams. 80 | */ 81 | class clientThread extends Thread { 82 | 83 | private String clientName = null; 84 | private ObjectInputStream is = null; 85 | private ObjectOutputStream os = null; 86 | private Socket clientSocket = null; 87 | private final ArrayList clients; 88 | public clientThread(Socket clientSocket, ArrayList clients) { 89 | 90 | this.clientSocket = clientSocket; 91 | this.clients = clients; 92 | 93 | } 94 | 95 | 96 | public void run() { 97 | 98 | ArrayList clients = this.clients; 99 | 100 | try { 101 | /* 102 | * Create input and output streams for this client. 103 | */ 104 | is = new ObjectInputStream(clientSocket.getInputStream()); 105 | os = new ObjectOutputStream(clientSocket.getOutputStream()); 106 | 107 | String name; 108 | while (true) { 109 | 110 | synchronized(this) 111 | { 112 | this.os.writeObject("Please enter your name :"); 113 | this.os.flush(); 114 | name = ((String) this.is.readObject()).trim(); 115 | 116 | if ((name.indexOf('@') == -1) || (name.indexOf('!') == -1)) { 117 | break; 118 | } else { 119 | this.os.writeObject("Username should not contain '@' or '!' characters."); 120 | this.os.flush(); 121 | } 122 | } 123 | } 124 | 125 | /* Welcome the new the client. */ 126 | 127 | System.out.println("Client Name is " + name); 128 | 129 | this.os.writeObject("*** Welcome " + name + " to our chat room ***\nEnter /quit to leave the chat room"); 130 | this.os.flush(); 131 | 132 | this.os.writeObject("Directory Created"); 133 | this.os.flush(); 134 | synchronized(this) 135 | { 136 | 137 | for (clientThread curr_client : clients) 138 | { 139 | if (curr_client != null && curr_client == this) { 140 | clientName = "@" + name; 141 | break; 142 | } 143 | } 144 | 145 | for (clientThread curr_client : clients) { 146 | if (curr_client != null && curr_client != this) { 147 | curr_client.os.writeObject(name + " has joined"); 148 | curr_client.os.flush(); 149 | 150 | } 151 | 152 | } 153 | } 154 | 155 | /* Start the conversation. */ 156 | 157 | while (true) { 158 | 159 | this.os.writeObject("Please Enter command:"); 160 | this.os.flush(); 161 | 162 | String line = (String) is.readObject(); 163 | 164 | 165 | if (line.startsWith("/quit")) { 166 | 167 | break; 168 | } 169 | 170 | /* If the message is private sent it to the given client. */ 171 | 172 | if (line.startsWith("@")) { 173 | 174 | unicast(line,name); 175 | 176 | } 177 | 178 | /* If the message is blocked from a given client. */ 179 | 180 | else if(line.startsWith("!")) 181 | { 182 | blockcast(line,name); 183 | } 184 | 185 | else 186 | { 187 | 188 | broadcast(line,name); 189 | 190 | } 191 | 192 | } 193 | 194 | /* Terminate the Session for a particluar user */ 195 | 196 | this.os.writeObject("*** Bye " + name + " ***"); 197 | this.os.flush(); 198 | System.out.println(name + " disconnected."); 199 | clients.remove(this); 200 | 201 | 202 | synchronized(this) { 203 | 204 | if (!clients.isEmpty()) { 205 | 206 | for (clientThread curr_client : clients) { 207 | 208 | 209 | if (curr_client != null && curr_client != this && curr_client.clientName != null) { 210 | curr_client.os.writeObject("*** The user " + name + " disconnected ***"); 211 | curr_client.os.flush(); 212 | } 213 | 214 | 215 | 216 | 217 | } 218 | } 219 | } 220 | 221 | 222 | this.is.close(); 223 | this.os.close(); 224 | clientSocket.close(); 225 | 226 | } catch (IOException e) { 227 | 228 | System.out.println("User Session terminated"); 229 | 230 | } catch (ClassNotFoundException e) { 231 | 232 | System.out.println("Class Not Found"); 233 | } 234 | } 235 | 236 | 237 | 238 | /**** This function transfers message or files to all the client except a particular client connected to the server ***/ 239 | 240 | void blockcast(String line, String name) throws IOException, ClassNotFoundException { 241 | 242 | String[] words = line.split(":", 2); 243 | 244 | /* Transferring a File to all the clients except a particular client */ 245 | 246 | if (words[1].split(" ")[0].toLowerCase().equals("sendfile")) 247 | { 248 | byte[] file_data = (byte[]) is.readObject(); 249 | 250 | synchronized(this) { 251 | for (clientThread curr_client : clients) { 252 | if (curr_client != null && curr_client != this && curr_client.clientName != null 253 | && !curr_client.clientName.equals("@"+words[0].substring(1))) 254 | { 255 | curr_client.os.writeObject("Sending_File:"+words[1].split(" ",2)[1].substring(words[1].split("\\s",2)[1].lastIndexOf(File.separator)+1)); 256 | curr_client.os.writeObject(file_data); 257 | curr_client.os.flush(); 258 | 259 | 260 | } 261 | } 262 | 263 | /* Echo this message to let the user know the blocked file was sent.*/ 264 | 265 | this.os.writeObject(">>Blockcast File sent to everyone except "+words[0].substring(1)); 266 | this.os.flush(); 267 | System.out.println("File sent by "+ this.clientName.substring(1) + " to everyone except " + words[0].substring(1)); 268 | } 269 | } 270 | 271 | /* Transferring a message to all the clients except a particular client */ 272 | 273 | else 274 | { 275 | if (words.length > 1 && words[1] != null) { 276 | words[1] = words[1].trim(); 277 | if (!words[1].isEmpty()) { 278 | synchronized (this){ 279 | for (clientThread curr_client : clients) { 280 | if (curr_client != null && curr_client != this && curr_client.clientName != null 281 | && !curr_client.clientName.equals("@"+words[0].substring(1))) { 282 | curr_client.os.writeObject("<" + name + "> " + words[1]); 283 | curr_client.os.flush(); 284 | 285 | 286 | } 287 | } 288 | /* Echo this message to let the user know the blocked message was sent.*/ 289 | 290 | this.os.writeObject(">>Blockcast message sent to everyone except "+words[0].substring(1)); 291 | this.os.flush(); 292 | System.out.println("Message sent by "+ this.clientName.substring(1) + " to everyone except " + words[0].substring(1)); 293 | } 294 | } 295 | } 296 | } 297 | } 298 | 299 | /**** This function transfers message or files to all the client connected to the server ***/ 300 | 301 | void broadcast(String line, String name) throws IOException, ClassNotFoundException { 302 | 303 | /* Transferring a File to all the clients */ 304 | 305 | if (line.split("\\s")[0].toLowerCase().equals("sendfile")) 306 | { 307 | 308 | byte[] file_data = (byte[]) is.readObject(); 309 | synchronized(this){ 310 | for (clientThread curr_client : clients) { 311 | if (curr_client != null && curr_client.clientName != null && curr_client.clientName!=this.clientName) 312 | { 313 | curr_client.os.writeObject("Sending_File:"+line.split("\\s",2)[1].substring(line.split("\\s",2)[1].lastIndexOf(File.separator)+1)); 314 | curr_client.os.writeObject(file_data); 315 | curr_client.os.flush(); 316 | 317 | } 318 | } 319 | 320 | this.os.writeObject("Broadcast file sent successfully"); 321 | this.os.flush(); 322 | System.out.println("Broadcast file sent by " + this.clientName.substring(1)); 323 | } 324 | } 325 | 326 | else 327 | { 328 | /* Transferring a message to all the clients */ 329 | 330 | synchronized(this){ 331 | 332 | for (clientThread curr_client : clients) { 333 | 334 | if (curr_client != null && curr_client.clientName != null && curr_client.clientName!=this.clientName) 335 | { 336 | 337 | curr_client.os.writeObject("<" + name + "> " + line); 338 | curr_client.os.flush(); 339 | 340 | } 341 | } 342 | 343 | this.os.writeObject("Broadcast message sent successfully."); 344 | this.os.flush(); 345 | System.out.println("Broadcast message sent by " + this.clientName.substring(1)); 346 | } 347 | 348 | } 349 | 350 | } 351 | 352 | /**** This function transfers message or files to a particular client connected to the server ***/ 353 | 354 | void unicast(String line, String name) throws IOException, ClassNotFoundException { 355 | 356 | String[] words = line.split(":", 2); 357 | 358 | /* Transferring File to a particular client */ 359 | 360 | if (words[1].split(" ")[0].toLowerCase().equals("sendfile")) 361 | { 362 | byte[] file_data = (byte[]) is.readObject(); 363 | 364 | for (clientThread curr_client : clients) { 365 | if (curr_client != null && curr_client != this && curr_client.clientName != null 366 | && curr_client.clientName.equals(words[0])) 367 | { 368 | curr_client.os.writeObject("Sending_File:"+words[1].split(" ",2)[1].substring(words[1].split("\\s",2)[1].lastIndexOf(File.separator)+1)); 369 | curr_client.os.writeObject(file_data); 370 | curr_client.os.flush(); 371 | System.out.println(this.clientName.substring(1) + " transferred a private file to client "+ curr_client.clientName.substring(1)); 372 | 373 | /* Echo this message to let the sender know the private message was sent.*/ 374 | 375 | this.os.writeObject("Private File sent to " + curr_client.clientName.substring(1)); 376 | this.os.flush(); 377 | break; 378 | 379 | } 380 | } 381 | } 382 | 383 | /* Transferring message to a particular client */ 384 | 385 | else 386 | { 387 | 388 | if (words.length > 1 && words[1] != null) { 389 | 390 | words[1] = words[1].trim(); 391 | 392 | 393 | if (!words[1].isEmpty()) { 394 | 395 | for (clientThread curr_client : clients) { 396 | if (curr_client != null && curr_client != this && curr_client.clientName != null 397 | && curr_client.clientName.equals(words[0])) { 398 | curr_client.os.writeObject("<" + name + "> " + words[1]); 399 | curr_client.os.flush(); 400 | 401 | System.out.println(this.clientName.substring(1) + " transferred a private message to client "+ curr_client.clientName.substring(1)); 402 | 403 | /* Echo this message to let the sender know the private message was sent.*/ 404 | 405 | this.os.writeObject("Private Message sent to " + curr_client.clientName.substring(1)); 406 | this.os.flush(); 407 | break; 408 | } 409 | } 410 | } 411 | } 412 | } 413 | } 414 | 415 | 416 | } 417 | 418 | 419 | 420 | 421 | --------------------------------------------------------------------------------