Subversion Repositories programming

Rev

Rev 217 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*******************************************************************************
 * File: P2_Client.java
 * Author: Ira W. Snyder (devel@irasnyder.com)
 * License: GNU General Public License v2
 * Class: CS380 - Computer Networking
 *
 * Assignment: Project #2
 * Date Last Modified: 2006-02-08
 *
 * Purpose: Implement a simple client allowing the user to send datagrams
 * to P2_Server, which will decode them from 4B to 5B encoding. The client
 * will retransmit any packets that do not recieve a reply.
 ******************************************************************************/

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

public class P2_Client
{
    private static int soTimeout = 3000; // 3000 ms timeout = 3 sec
    private static DatagramSocket socket;
    private static DatagramPacket packet;
    private static int portNumber = 1337;
    private static int packetNum = 0;

    /**
     * Method: verifyInput()
     * Purpose: Verify the user's input before it is sent out to the
     * server for translation.
     */
    public static boolean verifyInput (String s)
    {
        int i;
        
        // Check for QUIT message
        if (s.equals("QUIT"))
            return true;

        // If it's not a QUIT, it must be 0's and 1's only
        for (i=0; i<s.length(); i++)
            if (!(s.charAt(i) == '0' || s.charAt(i) == '1'))
                return false;

        // Everything was a 0 or 1, good!
        return true;
    }

    /**
     * Method: readPacket()
     * Purpose: Read a packet on the currently opened DatagramSocket,
     * and return it as a P2_Packet.
     */
    public static P2_Packet readPacket ()
    {
        byte[] buf = new byte[256];
        packet = new DatagramPacket (buf, buf.length);

        // Try to recieve a packet from the server, and catch all possible
        // exceptions that can happen.
        try {
            socket.receive (packet);
        } catch (SocketTimeoutException e) {
            System.out.println ("Read timed out, requesting retransmit");
            return null;
        } catch (PortUnreachableException e) {
            System.out.println ("Caught PortUnreachableException, are you" +
                                " sure the server is running?");
            System.exit(1);
        } catch (IOException e) {
            System.out.println ("Caught exception in readPacket()");
            e.printStackTrace();
            return null;
        }

        // Convert the bytes to a P2_Packet, then return them.
        return new P2_Packet (packet.getData());
    }

    /**
     * Method: writePacket()
     * Purpose: Send a packet to localhost:portNumber.
     */
    public static boolean writePacket (byte type, byte[] data, int packetNum)
    {
        InetAddress address = null;
        int port = portNumber;

        // Try to get the localhost address. If this fail's, we've got major
        // problems going on, so just exit.
        try {
            address = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            System.err.println ("Caught UnknownHostException in writePacket(), " +
                                "are you sure the system is working?");
            System.exit(1);
        }

        // Create the new packet
        byte[] buf = P2_Packet.createPacket (type, data, packetNum);
        packet = new DatagramPacket (buf, buf.length, address, port);

        // Try to send the packet, and catch all of the possible errors.
        try {
            socket.send (packet);
        } catch (PortUnreachableException e) {
            System.out.println ("Caught PortUnreachableException, are " +
                                "you sure the server is running?");
            System.exit(1);
        } catch (IOException e) {
            System.out.println ("Caught exception in writePacket()");
            e.printStackTrace();
            return false;
        }

        // We sent successfully, so return true.
        return true;
    }

    /**
     * Method: guaranteedTransmit()
     * Purpose: A wrapper around readPacket() and writePacket() that implements
     * a reliable transmit mechanism.
     */
    public static P2_Packet guaranteedTransmit (byte type, byte[] data)
    {
        P2_Packet reply;
        int retries = 0;

        // Send a packet, and wait for a reply
        writePacket (type, data, packetNum);
        reply = readPacket ();

        // If the read timed out, keep trying to send until it gets through
        while (reply == null)
        {
            System.err.println ("Packet lost, retransmitting...");
            writePacket (type, data, packetNum);
            reply = readPacket ();
            retries++;
        }

        // Reset the packetNum if we had to retry sending a packet.
        // The packetNum is used to control the server's dropping
        // of packets.
        if (retries > 0)
            packetNum = 0;

        packetNum++;
        return reply;
    }

    /**
     * Method: guaranteedTransmit()
     * Purpose: A simplified version of the guaranteedTransmit() above.
     */
    public static P2_Packet guaranteedTransmit (byte type)
    {
        byte[] pData = { 0 };
        
        return guaranteedTransmit (type, pData);
    }
    
    /**
     * Method: main()
     * Purpose: This will accept input from the user, and attempt to send it
     * to the corresponding server, which will convert the messages from 4B
     * to 5B encoding.
     */
    public static void main (String[] args) throws Exception
    {
        int dropHowMany = 0;
        socket = new DatagramSocket();
        socket.connect (InetAddress.getLocalHost(), portNumber);

        // Set the socket's timeout to 3 seconds
        socket.setSoTimeout (soTimeout);

        BufferedReader kbd = new BufferedReader (
                                 new InputStreamReader (
                                     System.in));

        // Parse the first command-line argument into an int, and use that
        // to inform the server how often to drop packets.
        try {
            dropHowMany = Integer.parseInt(args[0]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.err.println ("You need to call the program with \'java P2_Client <num>\'");
            System.exit(1);
        }

        String msg;
        byte[] pData = { (byte)dropHowMany };
        P2_Packet reply = guaranteedTransmit (P2_Packet.DROP, pData);

        // Check to make sure our packet informing the server how often to drop
        // packets was recieved successfully.
        if (reply.getPacketType() != P2_Packet.DROP)
        {
            System.err.println ("Unable to inform the server how often to drop packets");
            System.exit(1);
        }

        boolean done = false;

        // Keep running until the user decides that they don't want to
        // decode any more 4B numbers.
        while (!done)
        {
            System.out.print ("% decode: ");
            msg = kbd.readLine ();

            // Check user input
            if (!verifyInput(msg))
            {
                System.out.println ("Bad input, try again...");
                continue;
            }
            
            // Check for a QUIT message
            if (msg.equals("QUIT"))
            {
                reply = guaranteedTransmit (P2_Packet.QUIT);
                done = true;
                continue;
            }
            
            reply = guaranteedTransmit (P2_Packet.TRANSREQ, P2_EncDec.convertToByteArray(msg));

            // Check for a bad packet from the server
            if (reply.getPacketType() == P2_Packet.BADPACKET)
                System.out.println ("-> reply: BAD REQUEST");
            else
                System.out.println ("-> reply: " + 
                        P2_EncDec.convertToString(reply.getPacketData()));
        }

        // Close the socket
        socket.close();
    }
}