Outline of the HUMP client program main loop.
    
    HUMP, unlike BUMP, does not have an UNLATCHED state. However, we will say
    that the state before ACK[0] has been sent is UNCONNECTED, followed by a
    CONNECTED state.
    
      - UNCONNECTED, until we have received the handoff packet and sent ACK[0]
        to the new port (allowing the server to send DATA[1]) 
 
      - CONNECTED, once we've sent ACK[0]
 
      - DALLY, after we've received the final data packet
 
    
    Here's a first pass at a pseudocode outline of the main body of the program;
    while it is pseudocode, note that while(true)
    and continue are legitimate
    java. Note how the use of continue
    makes elses unnecessary. 
    
    There are three main problems here: dally() is unspecified, the transition
    from UNLATCHED to LATCHED is unclear (and the program does not implement it
    correctly, clear or not), and the timeout-event handling is incomplete. 
    
    while (true) {
            replyDG =
    s.receive()    // possibly a timeout
            if TIMEOUT:
                   
    retransmit previous packet (ACK or REQ)
                   
    continue
            wrong IP addr:
                   
    continue
            wrong port:   
            // really separate from wrong IP-addr
                   
    send error packet
                   
    continue
            wrong length:    //
    can't even check for DATA opcode if there aren't enough bytes!
                   
    continue
            wrong protocol:
                   
    continue
            wrong opcode (not DATA):
                   
    continue
            create DATA packet out of replyDG
            wrong blocknum:
                   
    continue
            // now we have a good packet!
                   
    write data
                   
    expected_block ++;
                   
    send ACK to destport
                   
    if (size < 512) {
                   
            dally();   
            // to be discussed
                   
            break;   
            // done
                   
    }
    }
    
    The first steps are to implement the size < 512 check (but use a symbolic
    constant!), and to implement the sanity checks above.
    
    Now on to the timeout issue. There are two different uses of the term here.
    Suppose the timeout period is, say, 2000 ms.
    
      - A SocketTimeoutException, meaning that the socket received nothing at
        all for 2000 ms. Let's call this a "hard timeout".
 
      - It's time to resend. We are expecting Data[N] and haven't received it
        for at least 2000 ms (though we may have received other things). Let's
        call this a "soft timeout".
 
    
    It should be clear that a 2000-ms hard timeout does imply a soft timeout: if
    you've received nothing, then you certainly haven't received the packet you
    were waiting for. However, the converse
      is not true! It is possible for you to receive a steady stream of
    "noise" packets, that serve to prevent a hard timeout from ever occuring,
    but because none of them is the correct packet you still have to have a soft
    timeout. (It is also possible to set the hard timeout interval to a much
    smaller value, even 500 ms, but then rely on elapsed-time checks to
    determine when to retransmit.)
    
    We implement soft timeouts by checking the elapsed time. The current time is
    always available in System.currentTimeMillis(); you will save that value
    each time you send an ACK packet (including ACK[0]):
            send_time =
    System.currentTimeMillis();
    Next, we check at the top of the while loop, before even
    receiving a packet, that the elapsed time has not been exceeded. If you do
    this to check elapsed time for soft timeouts, you no longer really need to
    do anything for hard timeouts (except continue).
    The hard-timeout interval becomes the clock granularity, in effect: if the
    soft-timeout interval is 2000 ms and the hard-timeout interval is 1000 ms,
    then in the worst case you wait until 2000+1000 = 3000 ms before actually
    noticing and responding to the soft timeout. 
    
    After every "hard" timeout you check the elapsed time for a "soft" timeout.
    At this point, a (short) hard timeout no longer implies a (long) soft
    timeout.
    
    Note that, although the elapsed-time check is at the beginning of the loop
    here, it's never executed immediately after receiving a valid packet because
    after receiving a valid packet (and sending an ACK) we always update
    send_time, and the elapsed-time check will then fail until we've had at
    least once attempt at s.receive(). In other words, you don't need a flag or
    any special logic to prevent checking the elapsed time immediately after
    sending: it's harmless then. 
     Send REQ
      get HANDOFF
      Send ACK[0]
        send_time = System.currentTimeMilllis();
    while (true) {
           
      check elapsed time: if exceeded (ie if a soft timeout),
           
              resend whatever was sent most
      recently (always in ackDG, which makes this very simple)
           
              send_time =
      System.currentTimeMilllis();
            replyDG =
    s.receive()    // possibly a timeout
            if HARD_TIMEOUT:
           
              do nothing!
                   
    continue
            wrong IP addr:
                   
    continue
            wrong port:   
            // really separate from wrong IP-addr
                   
    send error packet
                   
    continue
            wrong length:    //
    can't even check for DATA opcode if there aren't enough bytes!
                   
    continue
            other checks:
                   
    continue
            not DATA
                   
    continue
            create DATA packet out of replyDG
            wrong blocknum:
                   
    continue
            // now we have a good packet!
            // this
      is the part you would modify to implement sliding windows
                   
    write data
                   
    expected_block ++;
                   
    send ACK to destport
           
              send_time =
      System.currentTimeMilllis();       
            
                   
    if (size < 512) {
                   
            dally();   
            // to be discussed
                   
            break;   
            // done
                   
    }
     }
    To implement sliding windows, you will change how you
      deal with a "good" packet, which is now a packet within the receive
      window. See the sample code in Chapter 6 of my book. If the arriving
      packet is at the bottom end of the window (blocknum = LAST_ACKED+1, then
      write that block, and then write any stored blocks that have contiguous
      numbering. Then send an ACK for the highest-numbered stored block you have
      just sent, and set send_time.
    If the arriving packet is in the window but not at the bottom, then store
      it somewhere. Do not send an ACK for the just-arriving packet. You can
      resend a duplicate ACK[LAST_ACKED] if you wish, in which case you would
      again update send_time.