URL: https://linuxfr.org/users/kennux/journaux/server-chat Title: Server chat Authors: kennux Date: 2003年12月15日T17:04:20+01:00 Tags: Score: 0 /** * Chat avance java cote serveur * 12/11/2003 * Remi Wipliez && Raphael Mariage. * version 1.0 * La classe comprend le main et la definition des Threads, un thread par client */ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramSocket; import java.net.DatagramPacket; import java.net.InetAddress; import java.util.Hashtable; import java.util.StringTokenizer; import java.lang.Thread; import java.util.Enumeration; import java.util.Vector; import java.util.GregorianCalendar; /* l'objet info_client contient toutes les informations "vitales" pour l'identification du client, c'est a dire qu'a chaque client est * associe un objet Info-client avec tous ces parametres */ class Info_client { int portcl; String nick, passwd, groupe; InetAddress adrcl; boolean connect; int cantalk=0, grade=0; public Info_client(String nickname, String pass, InetAddress adr, int port, boolean connexion, int compteur, int grad) { nick = nickname; adrcl = adr; portcl = port; passwd = pass; connect = connexion; cantalk = compteur; grade = grad; } } class Group { String nom, proprio; Vector membres; public Group(String name, String creat, Vector members) { nom = name; proprio = creat; membres = members; } } public class ServerUDP { static int ind1, lg, serv_port, max_cl = 30, taille_buff = 10240, ind=0, cl_port, dest_port, k=0, port2; static String serv_address, serv_port_tmp, chaine, ligne, adr="", login="", mess_recu="0", date, time, sepa, cmde="", line, pass, fonction; static String renvoi_exit, user, renvoi, argu="", commande="", argument="", message="", cl, passwd="", cle, port="", nouveaucl, passcrypte; static String[] info = new String[2]; static char tiret; static Hashtable table_clients = new Hashtable(), table_groupes = new Hashtable(); static DatagramPacket dp, dpmdp, dpok, dpbadlog, dpexist; static DatagramSocket ds; static byte buffer[] = new byte[taille_buff]; static StringTokenizer str_tok, infos; static InetAddress adr_cl, adr_dest, cl_adr, ia; static Info_client info_cl, desti, client_t, recup, stock, majinfo, obj_exit; static boolean pres = false, test=true, connect; static Enumeration listekeys; static BufferedReader br; static Vector client = new Vector(); public static void main(String [] args) { /* detection d'arguments passes en ligne de commande */ lg = args.length; if (lg > 0) { for (int i=0;i<lg;i++) if (args[i].charAt(0) == '-') { try { switch(args[i].charAt(1)) { case 'p': { serv_port = Integer.parseInt(args[i+1]); break; } default:{ System.out.println("Option inconnue : " + args[i]); System.out.println("Options possibles : "); System.out.println("-p : definir un port "); System.out.println(args[i].charAt(1)); System.out.println(args[i+1]); System.exit(0); } } } catch(ArrayIndexOutOfBoundsException e) { System.out.println("Argument manquant..."); System.exit(0); } } } else { serv_port = 2222; } /* fin de la detection des arguments */ table_clients = ControlServer.set_table_clients(); table_groupes = ControlServer.setgroups(); /* creation d'un socket avec le port du server */ ds = ConnectUDP.dsocket(serv_port); dp = ConnectUDP.dpacket(buffer, taille_buff, null, 0); /* lancement d'un thread dit de controle, c'est a dire permettant le controle local du server (afficher la table, la remettre a 0, * kick un client, ... */ new Control(table_clients, ds); /* boucle infinie de detection d'un nouveau client */ while(true && !mess_recu.equals("")) { /* on recupere la chaine envoyee par le client */ mess_recu = ConnectUDP.recep(ds, dp); str_tok = new StringTokenizer(mess_recu,"@"); /* recuperation des informations client : login, InetAddress et port*/ login = ""; cl_port = dp.getPort(); cl_adr = dp.getAddress(); try { login = str_tok.nextToken(); sepa = str_tok.nextToken(); cmde = str_tok.nextToken(); } catch(java.util.NoSuchElementException j) { } /*on teste la presence du client ds la table clients*/ boolean estpres = table_clients.containsKey(login); /* s'il est present, c'est qu'il s'est deja identifie au moins une fois */ if (estpres) { recup = (Info_client)table_clients.get(login); passcrypte = recup.passwd; /* s'il n'est pas connecte (c'est a dire la variable connect de l'objet Info-clients = 0*/ if (!recup.connect) { /* s'il y a un mot de passe, c'est que ke client s'est deja identifie */ if (!recup.passwd.equals("")) { /* si le debut du pass = new, c'est qu'un nouveau client n'a pas encore confirme son nouveau pass */ if (!recup.passwd.substring(0,3).equals("new")) { /* le client n'est pas connecte (booleen connecte = false) */ if (!recup.connect) { /* si le port = 0, c'est une reconnexion */ if (recup.portcl == 0) { /* on place donc le nouveau port et la nouvelle adresse dans l'objet client */ recup.adrcl = cl_adr; recup.portcl = cl_port; buffer = new byte[taille_buff]; fonction = "mdp"; buffer = fonction.getBytes(); dpmdp = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpmdp, cl_adr, cl_port); } /* si le port est different de 0, c'est que le client a demande a se connecter * et il vient de rentrer son mot de passe */ else { /* si le mot de passe est correct, on connecte le client en passant la variable connect * a true */ if (recup.passwd.equals(cmde)) { recup.connect = true; /* la variable cantalk indique quand le client a totalement fini de s'identifier * 0 = jamais parle * 1 = a rentre son login * 2 = a rentre son login et son mot de passe et peut maintenant discuter sur le chat. */ recup.cantalk = 1; buffer = new byte[taille_buff]; fonction = "ok"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); /* on a accepte le client, on lui a envoye "ok" donc on peut l'ajouter a la bdd */ table_clients.remove(login); table_clients.put(recup.nick, recup); } /* sinon on envoie un signe comme quoi le pass est faux */ else { buffer = new byte[taille_buff]; fonction = "badpass"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); } } } } /* ici, si le debut du pass est new, c'est ke le client vient d'envoyer sa confirmation de mdp */ else { /* si les 2 pass sont egaux on valide, et on ajoute le client a la table et on le connecte */ String passtmp = recup.passwd; String passtmp2 = passtmp.substring(3, passtmp.length()); if (passtmp2.equals(cmde)) { recup.passwd = cmde; recup.connect = true; recup.cantalk=1; fonction = "ok"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); table_clients.remove(login); table_clients.put(recup.nick, recup); /* mise a jour du fichier, c'est a dire rajoute une ligne contenant le login et le * mot de passe du nouveau client */ ControlServer.exec("maj", ds); } /* si les 2 pass sont differents, le client s'est trompe on redemande */ else { recup.passwd = ""; fonction = "bad2pass"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); } } } /* si le mdp est nul, c'est un nouveau client qui vient d'entrer son 1er pass */ /* on lui envoie donc la demande de second mdp */ else { recup.passwd = "new" + cmde; fonction = "2ndpass"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); } } /* si le client est deja connecte */ else { /* si le port du client demandeur est different de celui enregistre dans la table (pour le client du * meme nom), c'est qu'il existe deja un client connecte avec ce login */ if (recup.portcl != cl_port) { fonction = "connected"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); /* creer un paquet null pour mettre a zero toutes les nouvelles infos, car le client qui a declenche ce message * (connected) ne PEUT pas se connecter */ recup = new Info_client("", "", null, 0, false, 0, 0); } /* si les 2 ports sont egaux, c'est le bon client */ else { /* si le debut du pass est egal a old, le client vient de donner son ancien mot de passe */ if (recup.passwd.substring(0,3).equals("old")) { /* l'ancien mot de passe est egal a celui rentre : on demande le nouveau */ if (recup.passwd.substring(3,recup.passwd.length()).equals(cmde)) { fonction = "newpass1"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); recup.passwd = "vachanger"; recup.cantalk = 0; } else { fonction = "badoldpass"; System.out.println("ancien = " + recup.passwd.substring(3,recup.passwd.length())); System.out.println("new = " + cmde); buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); } } /* si le mot de passe ne commence pas par old, soit il n'y a rien soit il est nul pour un changement */ else { /* si le debut du pass est nul, le client vient de donner son nouveau mot de passe */ if (recup.passwd.equals("vachanger")) { recup.passwd = ""; fonction = "newpass2"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); recup.passwd = "new" + cmde; } /* si le debut du pass est "new", le client vient de donner sa confirmation de nouveau mot de passe */ else { if (recup.passwd.substring(0,3).equals("new")) { /* les 2 nouveaux pass sont egaux, on procede alors au changement du pass */ if (recup.passwd.substring(3,recup.passwd.length()).equals(cmde)) { fonction = "newpass"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); recup.passwd = cmde; recup.cantalk=1; } /* les 2 nouveaux pass sont differents, ont ne peut proceder au changement et on remet l'ancien*/ else { fonction = "badnewpass"; buffer = fonction.getBytes(); dpok = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpok, cl_adr, cl_port); recup.passwd = "old" + passcrypte; } } } } } } } /* si le login n'est pas present, on teste si c'est un client qui veut s'identifier pour la 1ere fois * avec le mot cle new + login. Sinon c'est une erreur de frappe sur le login ou il n'existe pas (encore)*/ else { if (login.equals("new")) { /* on verifie ici que le nouveau login entre n'est pas deja utilise */ boolean exist = table_clients.containsKey(cmde); if (!exist) { /* les conditions remplies, on peut creer un nouvel objet avec l'adr du client, son port, la variable connect a false * la variable cantalk a 0, et un pass vierge car il n'en a encore rentre aucun */ info_cl = new Info_client(cmde, "", cl_adr, cl_port, false, 0,0); table_clients.put(info_cl.nick, info_cl); // penser ici a mettre le fichier client a jour buffer = new byte[taille_buff]; fonction = "mdp"; buffer = fonction.getBytes(); dpmdp = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpmdp, cl_adr, cl_port); } /* si le login existe, on envoie un message au client */ else { buffer = new byte[taille_buff]; fonction = "exist"; buffer = new byte[taille_buff]; buffer = fonction.getBytes(); dpexist = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpexist, cl_adr, cl_port); recup = new Info_client("", "", null, 0, false, 0,0); } } /* si le login n'est pas new le login est mauvais */ else { buffer = new byte[taille_buff]; fonction = "badlog"; buffer = new byte[taille_buff]; buffer = fonction.getBytes(); dpbadlog = ConnectUDP.dpacket(buffer, buffer.length, cl_adr, cl_port); ConnectUDP.envoi(ds, dpbadlog, cl_adr, cl_port); recup = new Info_client("", "", null, 0, false, 0,0); } } /*******************************************/ // fin de la procedure de detection client //TestClient(login, cl_adr, cl_port); GregorianCalendar day = new GregorianCalendar(); /* dans chaque ojbet client, la variable cantalk est passe a 2 apres un 1er passage ici, pour etre sur que le pass ou des infos * de connection ne s'affiche */ if (recup.cantalk == 2) { /* on teste si le client veut sortir ou pas */ if (cmde.equals("/exit")) { date = day.getTime().toString(); time = date.substring(11,20); renvoi_exit = login + " has exit at " + time; System.out.println(renvoi_exit); buffer = renvoi_exit.getBytes(); /* creation d'un nouveau packet avec la chaine a renvoyer */ dp = ConnectUDP.dpacket(buffer, buffer.length, null, 1); /* boucle qui envoit la chaine annoncant la sortie d'un client a chacun des clients connectes */ listekeys = table_clients.keys(); for (ind=0; ind<table_clients.size(); ind++) { cle = (String)listekeys.nextElement(); desti = (Info_client)table_clients.get(cle); adr_dest = (InetAddress)desti.adrcl; dest_port = desti.portcl; if (dest_port != 0) ConnectUDP.envoi(ds, dp, adr_dest, dest_port); } /* on enleve le client de la bdd info_client */ table_clients.remove(login); obj_exit = new Info_client(login, recup.passwd, null, 0, false, 0,0); table_clients.put(obj_exit.nick, obj_exit); } /* dans les autres cas, un message normal ou une commande */ else { /* ne rien faire si la chaine envoyee est nulle */ if (!cmde.equals("")) { /* test afin de reconnaitre une commande a l'aide du caracteres / (slash) */ if (cmde.charAt(0) != '/') { System.out.println(login + "> " + cmde); date = day.getTime().toString(); time = date.substring(11,20); renvoi = time + " " + login + "> " + cmde; buffer = renvoi.getBytes(); dp = ConnectUDP.dpacket(buffer, buffer.length, null, 1); /* c'est un message normal donc on l'envoie a tout le monde */ listekeys = table_clients.keys(); for (int i=0; i<table_clients.size(); i++) { cle = (String)listekeys.nextElement(); desti = (Info_client)table_clients.get(cle); login = desti.nick; adr_dest = desti.adrcl; dest_port = desti.portcl; if (dest_port != 0) ConnectUDP.envoi(ds, dp, adr_dest, dest_port); } } /* il s'agit ici du traitement de la chaine de caracteres contenant l'intitule de la * commande, le ou les arguments, le login de l'expediteur et le message */ else { try { k=0; /* chaque "partie" (login, argument(s), commande,...)est separee par un espace. * Ainsi on a 3 boucles while qui recupere chacun des parametres. Puis on reduit * au fur et a mesure les chaines avec substring jusqu'a obtenir les 3 parametres * dans des chaines separees */ while(cmde.charAt(k) != ' ') { commande = commande + cmde.charAt(k); k++; } argu = cmde.substring(k+1,cmde.length()); k=0; while(argu.charAt(k) != ' ') { argument = argument + argu.charAt(k); k++; } message = argu.substring(k+1, argu.length()); /* ensuite on lance l'interpretation de la commande */ Cmde.traitement(ds, login, commande, argument, message); } catch(java.lang.StringIndexOutOfBoundsException e) { if (commande.equals("/msg")) { System.out.println("usage de /msg : "); System.out.println("/msg dest message"); System.out.println("/msg dest1+dest2+dest3... message"); } else { Cmde.traitement(ds, login, commande, argument, message); login = ""; commande = ""; argument = ""; message = ""; } } message = ""; argu = ""; } } /* creation d'un nouveau paquet vierge pour reception */ buffer = new byte[taille_buff]; dp = ConnectUDP.dpacket(buffer, taille_buff, null, 1); } } if (recup.cantalk == 1) recup.cantalk = 2; } } } /* Thread d'attente d'entree au clavier pour la gestion interne du serveur (commande, sortie, kick, ...) */ class Control extends Thread { DatagramSocket ds; DatagramPacket dp; int taille_buff = 10240, sport, test2; String adrs, serv_quit, command="", test1, cle, kicke; InetAddress S_ia; Hashtable info; BufferedReader kbr; byte[] buffer = new byte[taille_buff]; Info_client desti, test; int dest_port; InetAddress adr_dest, test3; Enumeration listekeys; public Control(Hashtable tbl, DatagramSocket sock) { info = tbl; ds = sock; start(); } public void run() { /* boucle infinie qui attend les entrees clavier sur le serveur */ while(true) { System.out.print("/"); kbr = new BufferedReader(new InputStreamReader(System.in)); try { command = kbr.readLine(); } catch(java.io.IOException e) { System.out.println("erreur IO"); } ControlServer.exec(command, ds); } } }