        // A class that maintains the attributes of a TCP endpoint.
        // This class looks after the socket attribute and
        // buffered reader and writer associated with the
        // two streams of a TCP communication.
        // Typical clients and servers extend this class to provide
        // the functionality applicable to their application.

import java.net.*;
import java.io.*;

class TCPEndpoint  {
        // Contact the server at the appropriate port and set
        // the socket attribute for the run() method to use.
    public TCPEndpoint(String hostname,int port) throws Exception {
        try{
            Socket socket = new Socket(hostname,port);
            // Setup the socket attribute and associated streams.
            setSocket(socket);
        }
        catch(UnknownHostException e){
            throw new Exception("Unknown host: "+e.getMessage());
        }
        catch(IOException e){
            throw new Exception("IOException on socket creation: "+
                                        e.getMessage());
        }
    }

        // A connection has already been established.
        // Setup the socket attribute and associated streams.
    public TCPEndpoint(Socket socket) throws Exception {
        setSocket(socket);
    }

        // Get the next line from the server.
    protected String getLine() throws IOException {
        return getReader().readLine();
    }

        // Flush the given line to the server.
    protected void sendLine(String line) throws IOException {
        bufferLine(line);
        getWriter().flush();
    }

        // Send the given line to the server without flushing.
    protected void bufferLine(String line) throws IOException {
        BufferedWriter writer = getWriter();
        writer.write(line);
        writer.newLine();
    }
    
    // Close the socket.
    protected void close() {
        Socket sock = getSocket();
        try{
            sock.close();
        }
        catch(IOException e){
            // Not much we can do about this.
        }
    }

    protected Socket getSocket(){
        return socket;
    }

        // Setup the socket attribute and create an
        // associated Reader and Writer for the associated
        // streams.
    protected void setSocket(Socket s) throws Exception {
        if(s != null){
            socket = s;
            // Wrap the input stream in a BufferedReader.
            setReader(new BufferedReader(
                        new InputStreamReader(socket.getInputStream())));
            // Wrap the output stream in a BufferedWriter.
            setWriter(new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())));
        }
        else{
            throw new Exception("Null socket passed to setSocket.");
        }
    }

    protected BufferedReader getReader(){
        return reader;
    }

    protected void setReader(BufferedReader r){
        reader = r;
    }

    protected BufferedWriter getWriter(){
        return writer;
    }

    protected void setWriter(BufferedWriter w){
        writer = w;
    }

    private Socket socket;
    // Buffered reader and writer for the socket's streams.
    private BufferedReader reader;
    private BufferedWriter writer;
}
