El client es connecta amb el servidor i permet enviar missatges a altres clients.
El client té les següents commandes:
\exit: Ix del xat.
\msg alias message: Envia el missatge message a el client identificat per alias.
\change alias: Canvia el seu alias a alias.
\list: Li demana al servidor els cliens connectats i els mostra per pantalla.
El client té la classe MessagingClientListener, que s'encarrega de rebre
els missatges del servidor. Depen dels tipus de missatge, ha de mostrar
els missatges rebuts en el següent format:
packageexam2.server;importjava.io.IOException;importjava.net.ServerSocket;importjava.net.Socket;importjava.util.ArrayList;importjava.util.Scanner;importjava.util.stream.Collectors;importjava.util.List;publicclassMessagingServerextendsThread{ServerSocketserver;List<MessagingServerHandler>clients;booleanrunning;publicMessagingServer(intport)throwsIOException{// TODO: Crea un SocketServer mitjançant JSSEthis.server=null;clients=newArrayList<>();running=true;}publicvoidclose(){running=false;this.interrupt();}publicsynchronizedvoidremoveClient(MessagingServerHandlerhc){clients.remove(hc);}/** * Retorna el client amb el alies especificat o null si no existeix * @param alias Alias de la persona a la que li envies el missatge * @return Retorna el client amb el alies especificat o null si no existeix */publicMessagingServerHandlergetClientByAlias(Stringalias){returnclients.stream().filter(c->c.getAlias().equals(alias)).findAny().orElse(null);}/** * Retorna els alies dels clients connectats separats per comes * @return Alies dels clients connectats separats per comes */publicStringconnectedClients(){returnclients.stream().map(MessagingServerHandler::getAlias).collect(Collectors.joining(","));}@Overridepublicvoidrun(){while(running){try{Socketclient=server.accept();MessagingServerHandlermessagingServerHandler=newMessagingServerHandler(client,this);clients.add(messagingServerHandler);messagingServerHandler.start();System.out.println("Nova connexió acceptada.");}catch(IOExceptione){System.err.println("Error while accepting new connection");System.err.println(e.getMessage());}}}publicstaticvoidmain(String[]args){try{Scannerscanner=newScanner(System.in);MessagingServerserver=newMessagingServer(1234);server.start();scanner.nextLine();server.close();}catch(IOExceptione){thrownewRuntimeException(e);}}}
MessagingServerHandler.java
packageexam2.server;importexam2.models.MessagingMessage;importexam2.models.MessagingMessageType;importjava.io.IOException;importjava.net.Socket;publicclassMessagingServerHandlerextendsThread{privatefinalMessagingServerserver;privatefinalSocketclient;// TODO: ObjectStream related objectprivateStringalias;publicMessagingServerHandler(Socketclient,MessagingServerserver)throwsIOException{this.server=server;this.client=client;// TODO: ObjectStream related objectalias="";}publicStringgetAlias(){returnalias;}publicvoidsetAlias(Stringalias){this.alias=alias;}/** * TODO: Envia una petició * @param messagingMessage Petició a enviar * @throws IOException */publicvoidsendMessage(MessagingMessagemessagingMessage)throwsIOException{}/** * TODO: Llegeix una petició * @return Petició llegida * @throws IOException * @throws ClassNotFoundException */publicMessagingMessagereadMessage()throwsIOException,ClassNotFoundException{// TODOreturnnull;}/** * TODO: Acció del servidor a les respostes del tipus SEND * @param messagingMessage Petició a processar */privatevoidprocessSend(MessagingMessagemessagingMessage)throwsIOException{}/** * TODO: Acció del servidor a les respostes del tipus CHANGE_NAME * @param messagingMessage Petició a processar */privatevoidprocessChangeName(MessagingMessagemessagingMessage)throwsIOException{}/** * TODO: Acció del servidor a les respostes del tipus LIST * @param messagingMessage Petició a processar */privatevoidprocessList(MessagingMessagemessagingMessage)throwsIOException{}@Overridepublicvoidrun(){try{MessagingMessagemessagingMessage;while((messagingMessage=readMessage())!=null){System.out.println(messagingMessage);switch(messagingMessage.type()){caseSEND->{processSend(messagingMessage);}caseCHANGE_NAME->{processChangeName(messagingMessage);}caseLIST->{processList(messagingMessage);}}}}catch(IOException|ClassNotFoundExceptione){System.err.println(e.getMessage());}server.removeClient(this);}}
packageexam2.models;importjava.io.Serializable;/** * Classe que representa una petició o resposta entre el servidor i el client. * <p> * Aquesta classe implementa Serialitzable per poder ser convertida a * bytes i poder ser enviada mitjançant sockets. * * @see MessagingMessageType * @param type Tipus de la petició * @param alias Destinatari * @param message Missatge adjuntat */publicrecordMessagingMessage(MessagingMessageTypetype,Stringalias,Stringmessage)implementsSerializable{@OverridepublicStringtoString(){return"Request{"+"type="+type+", alias='"+alias+'\''+", message='"+message+'\''+'}';}}
MessagingMessageType.java
packageexam2.models;importjava.io.Serializable;/** * Enumeració amb els diferents tipus de peticions que podem trobar * <p> * Aquesta classe implementa Serialitzable per poder ser convertida a * bytes i poder ser enviada mitjançant sockets. */publicenumMessagingMessageTypeimplementsSerializable{/** * El tipus LIST s'utilitza per indicar al servidor que es vol recuperar la llista de clients connectats */LIST,/** * El tipus CHANGE_NAME s'utilitza per canviar el àlias del client */CHANGE_NAME,/** * El tipus USER_ALREADY_EXISTS_ERROR s'utilitza per indicar que ja existeix un usuari amb aquest àlias */USER_ALREADY_EXISTS_ERROR,/** * El tipus SEND s'utilitza per enviar un missatge */SEND,/** * El tipus USER_NOT_FOUND_ERROR s'utilitza per indicar que l'usuari no existeix */USER_NOT_FOUND_ERROR,/** * El tipus INVALID_MESSAGE_ERROR s'utilitza per indicar que el missatge no és vàlid */INVALID_MESSAGE_ERROR,/** * El tipus SUCCESS s'utilitza per indicar que l'acció s'ha dut a terme correctament */SUCCESS,}
packageexam2.client;importexam2.models.MessagingMessage;importjava.io.IOException;importjava.net.Socket;importjava.util.Scanner;publicclassMessagingClient{privatefinalSocketsocket;privatefinalMessagingClientListenerlistener;privatefinalScannerscanner;// TODO: ObjectStream related objectpublicMessagingClient(Stringhost,intport)throwsIOException{this.scanner=newScanner(System.in);// TODO: Connectar-se mitjançant JSSEthis.socket=null;// TODO: Object stream related objectsthis.listener=newMessagingClientListener(socket,this);this.listener.start();}/** * TODO: Envia una petició * @param messagingMessage Petició a enviar * @throws IOException */publicvoidsendMessage(MessagingMessagemessagingMessage)throwsIOException{}/** * TODO: Demana a l'usuari el seu àlies i s'identifica amb el servidor * @throws IOException */publicvoididentify()throwsIOException{System.out.print("Introdueix el teu nom: ");Stringline=scanner.nextLine();}publicvoidchat()throwsIOException{System.out.println("Acabes d'entrar al chat.");System.out.println("Per exir, escriu \"/exit\".");Stringaction;while((action=scanner.next())!=null&&this.socket.isConnected()){switch(action){case"/exit"->{this.close();return;}case"/list"->{list();}case"/change"->{Stringalias=scanner.nextLine();change(alias);}case"/msg"->{Stringalias=scanner.next();Stringmessage=scanner.nextLine();msg(alias,message);}default->{System.out.printf("Ordre \"%s\" no trobada\n",action);scanner.nextLine();}}}}privatevoidlist()throwsIOException{// TODO: /list}privatevoidchange(Stringalias)throwsIOException{// TODO: /change alias}privatevoidmsg(Stringalias,Stringmessage)throwsIOException{// TODO: /msg alias message}publicvoidclose(){try{socket.close();listener.interrupt();}catch(IOExceptione){thrownewRuntimeException(e);}}publicstaticvoidmain(String[]args){System.out.println("Connectant-se amb el servidor...");try{MessagingClientchat=newMessagingClient("localhost",1234);chat.identify();chat.chat();}catch(IOExceptione){System.err.println("Error connectant-se amb el servidor.");}}}
MessagingClientListener.java
packageexam2.client;importexam2.models.MessagingMessage;importjava.io.IOException;importjava.net.Socket;publicclassMessagingClientListenerextendsThread{privatefinalMessagingClientclient;privatefinalSocketsocket;// TODO: ObjectStream related objectpublicMessagingClientListener(Socketsocket,MessagingClientclient)throwsIOException{this.client=client;this.socket=socket;// TODO: ObjectStream related object}/** * TODO: Llegeix una petició * @return Petició llegida * @throws IOException * @throws ClassNotFoundException */publicMessagingMessagereadMessage()throwsIOException,ClassNotFoundException{returnnull;}// TODO: Reb missatges@Overridepublicvoidrun(){try{MessagingMessagemessagingMessage;while((messagingMessage=readMessage())!=null){System.out.println(messagingMessage);switch(messagingMessage.type()){caseSEND->{processSend(messagingMessage);}caseSUCCESS->{processSuccess(messagingMessage);}caseUSER_NOT_FOUND_ERROR->{processUserNotFoundError(messagingMessage);}caseINVALID_MESSAGE_ERROR->{processInvalidMessageError(messagingMessage);}caseUSER_ALREADY_EXISTS_ERROR->{processUserAlreadyExistsError(messagingMessage);}}}}catch(IOException|ClassNotFoundExceptione){System.err.println(e.getMessage());}}privatevoidprocessSend(MessagingMessagemessagingMessage){// TODO: Acció del client a les respostes del tipus SEND}privatevoidprocessSuccess(MessagingMessagemessagingMessage){// TODO: Acció del client a les respostes del tipus SUCCESS}privatevoidprocessUserNotFoundError(MessagingMessagemessagingMessage){// TODO: Acció del client a les respostes del tipus USER_NOT_FOUND_ERROR}privatevoidprocessInvalidMessageError(MessagingMessagemessagingMessage){// TODO: Acció del client a les respostes del tipus INVALID_MESSAGE_ERROR}privatevoidprocessUserAlreadyExistsError(MessagingMessagemessagingMessage){// TODO: Acció del client a les respostes del tipus USER_ALREADY_EXISTS_ERROR}}