// ChatServe - a server which lets attached clients "chat" // with each other. The program has two threads: a main thread // which accepts client connections and "logs them in" by recording // information including screen name and socket id; and a // secondary thread, initiated by the first client to connect, // which reads UDP datagrams from sending clients and resends // the message to all other connected clients via TCP. // Run Chatserve with: // java ChatServe import java.io.*; import java.net.*; import java.util.*; class ChatServe { public static void main(String args[]) throws Exception { int chatServerTCPPort = 0; int chatServerUDPPort = 0; ServerSocket welcomeSocket = null; Socket connectionSocket = null; DatagramSocket chatReceiveSocket = null; String thisScreenName = null; ArrayList chatClientList = new ArrayList(); Vector chatClientInfo; if(args.length <2 ) { System.err.println("Usage: java \n"); System.exit(-1); } else { /* Check the TCP and UDP port numbers to make sure they are within the proper range */ try { chatServerTCPPort = Integer.parseInt(args[0]); chatServerUDPPort = Integer.parseInt(args[1]); if ((chatServerTCPPort <1024)||(chatServerTCPPort >65535)) { System.err.println("Port# must be a number between 1024 and 65535.\n"); System.exit(-1); } if ((chatServerUDPPort <1024)||(chatServerUDPPort >65535)) { System.err.println("Port# must be a number between 1024 and 65535.\n"); System.exit(-1); } } catch (NumberFormatException n) { System.err.println("Port# must be a number between 1024 and 65535.\n"); System.exit(-1); } } // end of if --- else clause for argument checking try { chatReceiveSocket = // set up socket } catch (IOException e) { System.err.println("Unable to connect on port " + chatServerUDPPort); } try { welcomeSocket = //set up socket } catch (IOException e) { System.err.println("Unable to connect on port " + chatServerTCPPort); } // MAIN LOOP - Loop while waiting for chat clients to "Log in" via an accepted connection on the // designated TCP port while(true) { connectionSocket = welcomeSocket.accept(); InetAddress clientIPAddress = connectionSocket.getInetAddress(); chatClientInfo = new Vector(); boolean uniqueName = true; DataOutputStream outToClient = null; BufferedReader inFromClient = null; try { outToClient = new DataOutputStream(connectionSocket.getOutputStream()); inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream())); thisScreenName = inFromClient.readLine(); // Verify that screeename is unique before adding this client to list Vector testInfo = new Vector(); for(int clientNumber = 0; clientNumber < chatClientList.size(); clientNumber++) { testInfo =(Vector)chatClientList.get(clientNumber); if (thisScreenName.equals((String)testInfo.get(1))) { // send 0 value for UDP port# to client to indicate duplicate screen name error outToClient.writeBytes("0\n"); uniqueName = false; break; } } } catch (IOException e){ System.err.println ("Problems with TCP connection to client."); } // If unique screen name give client UDP Port# and create an information vector for this client if (uniqueName == true) { outToClient.writeBytes(String.valueOf(chatServerUDPPort) + "\n"); chatClientInfo.add(0,clientIPAddress); chatClientInfo.add(1,thisScreenName); chatClientInfo.add(2,connectionSocket); /* add the vector to the ChatClientList */ synchronized (chatClientList) { chatClientList.add(chatClientInfo); } // Create the listener - "jabber" - thread on the first client connection if (chatClientList.size()<= 1) { try { Servant newServant = new Servant(connectionSocket, chatReceiveSocket, chatClientList); } catch (Exception e) { System.err.println ("Thread Exception when creating new client thread."); } } } } //end of while true loop } //end of method main }//end of class ChatServe // This class is the secondary thread of the server. It is invoked when the first valid client // connects, and continues to run until the server is terminated. It is responsible for receiving // chat messages from the attached clients via the UDP socket passed as an argument(UDPsock), and // resending the message to all attached clients as defined in the passed ArrayList argument // (clients) via the client's TCP connection. class Servant extends Thread { private Socket socketToClient; private DatagramSocket socketFromClients; private ArrayList listOfClients; private String chatMessage; private Vector thisClientInfo; // private int buffSize; public Servant (Socket TCPsock, DatagramSocket UDPsock, ArrayList clients) { socketToClient = TCPsock; socketFromClients = UDPsock; listOfClients = clients; start(); } public void run() { // Loop "forever" to read and re-braodcast datagram packets from chatting clients while (true) { try { byte[] chatInBuffer = new byte[512]; receivePacket = // set up new DatagramPacket // receive a packet // get the message and other info from the packet } catch (IOException e) { System.err.println("Exception with message (datagram) from client."); } // Write the screen name and chatmessage out to all connected clients, except sender try { // Loop through all clients in the list, sending to clients in "Chatwith" list for(int clientNumber =0; clientNumber < listOfClients.size(); clientNumber++) { try { // send to a client // This IOException indicates that the client is not connected, or the write-back // failed for some other reason. In that case, remove the client's entry from the list. // This logic works, but is problematic in that, if a client leaves and then returns // with the same screenname before another client has sent a message, they will receive // a "duplicate screename" error at start-up. } catch (IOException c) { System.err.println ("Write-back error. Removing Client " + (String)thisClientInfo.get(1)); synchronized (listOfClients) { listOfClients.remove(clientNumber); clientNumber = clientNumber -1; // otherwise, you will skip the next entry in the list } } } } } catch (IOException e) { System.err.println ("Exception writing to clients."); } } //end of main while true loop } // end of run method } //end of class Servant