/**************************************************************************************************
* Create a TCP/IP server (Using threads to create multiple connections)
* TCP server communication uses both a TCP server socket object and a TCP client socket object.
* A TCP server socket object is used to maintain an open socket connection to all clients.
* Using the accept() method, a TCP server socket will establish a new client socket.
* When a TCP client socket is created you are assigned a port number by the O/S (next port#).
* A new thread is created, and passed the client socket. 
* The new thread will handle the communication with the client 
*
* PS. This server takes whatever the client sends, and returns it in uppercase
*     This version of the server is threaded
*
* Usage: TCPserver <serverPort>
* 
* @version 1.0
* @author Sam Sultan
***************************************************************************************************/
import java.io.*;
import java.net.*;

public class TCPserver2
{
    public static void main(String[] args)
    {
        if (args.length != 1) {
            System.out.println("Usage: TCPserver2 <serverPort>");
            System.exit(0);
        }
    
        InetAddress  serverIP;          //IP address object for server    
        InetAddress  clientIP;          //IP address object for client

        int          serverPort;        //server port for listening
        int          clientPort;        //port number for client

        ServerSocket listenTCPsocket;   //TCP socket for listening
        Socket       TCPsocket;         //TCP socket for communication

        int i=0;

        try 
        {
            serverIP   = InetAddress.getLocalHost();    //get local server address
            serverPort = Integer.parseInt(args[0]);     //get server port number        

            System.out.println("Server Host Name.....: " + serverIP.getHostName());
            System.out.println("Server IP Address....: " + serverIP.getHostAddress());
            System.out.println("Server Port Requested: " + serverPort);

            listenTCPsocket = new ServerSocket(serverPort);     //socket for listening 

            System.out.println("Server Port Listen on: " + listenTCPsocket.getLocalPort());
            System.out.println();

            while(true)
            {
                System.out.println("Waiting for client connection(s)...... \n");

                TCPsocket = listenTCPsocket.accept();            //creating a Socket
                                                                 //from the ServerSocket
                i++;                
                System.out.println("Client " + i + " connection opened to:");
                
                clientIP    = TCPsocket.getInetAddress();       //get client IP address
                clientPort  = TCPsocket.getPort();              //get port assigned to client 

                System.out.println("Client Host Name.....: " + clientIP.getHostName());
                System.out.println("Client IP Address....: " + clientIP.getHostAddress());
                System.out.println("Client Port Assigned.: " + clientPort);
                System.out.println();

                Socket s = TCPsocket;				//assign to variable s  

                TCPserver2Thread t = new TCPserver2Thread(s);   //instantiate a thread, and
                                                                //pass it the client socket
                t.start();                                      //start the thread 
            }
        }                
        catch(IOException e)
        {
            System.out.println("I/O error: " + e);
            System.exit(-1);
        }
    }
}          

/**************************************************************************************************
* A threaded class that will be created for each client
* When the main thread detects a new client communication, it start a new thread
* Each thread will be responsible for a single client 
***************************************************************************************************/
class TCPserver2Thread extends Thread
{
    InetAddress clientIP;           //IP address object for client
    int         clientPort;         //port number for client
    Socket      TCPsocket;          //TCP socket for communication

    TCPserver2Thread(Socket s)                  //constructor 
    {
        TCPsocket = s;                          //save socket obj.
    }   

    public void run()
    {
        String recvString;                  //string received from client
        String sendString;                  //string to send client

        String threadName = this.getName();

        try 
        {
            clientIP    = TCPsocket.getInetAddress();       //get client IP address
            clientPort  = TCPsocket.getPort();              //get port assigned to client 

            InputStream        socketIn1 = TCPsocket.getInputStream();          //ref to socket input 
            InputStreamReader  socketIn2 = new InputStreamReader(socketIn1);    //convert to 16 bit
            BufferedReader     socketIn  = new BufferedReader(socketIn2);       //to use readLine()

            OutputStream       socketOut1 = TCPsocket.getOutputStream();        //ref to socket output 
            OutputStreamWriter socketOut2 = new OutputStreamWriter(socketOut1); //convert to 16 bit
            PrintWriter        socketOut  = new PrintWriter(socketOut2);        //buffered and can use println()

            while(true)
            {
                /*------------------ receiving data ---------------------*/
    

                System.out.println(threadName + " waiting for client input from.... "
                                              + clientIP + ":" + clientPort);
                System.out.println();

                recvString = socketIn.readLine();       //receive data from the socket

                if((recvString==null) || recvString.trim().length()==0)  //empty 
                    break;      

                System.out.println("Received..: " + recvString);

                /*------------------ sending data ---------------------*/
    
                sendString = recvString.toUpperCase();

                System.out.println("Sending...: " + sendString ); 
                System.out.println();

                socketOut.println(sendString + "\n\n");   //send data thru the socket with 2 newline     
                socketOut.flush();                        //flush buffer
            }

            System.out.println("Closing connection to: "+ clientIP +":"+ clientPort);
            socketIn.close();
            socketOut.close();
            TCPsocket.close();
        }
        catch(IOException e)
        {
            System.out.println("I/O error: " + e);
            System.exit(-1);
        }
    }
}