Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Outline of the wump client program main loop. There are three states: UNLATCHED,

ID: 3571557 • Letter: O

Question

Outline of the wump client program main loop.


There are three states:

UNLATCHED, until we get a good DATA[1] from the new port

LATCHED, once we've latched on to the new port

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, as presented on June 6; 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, well, 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
        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 fix is to clarify when we become LATCHED. This occurs when we recognize a valid DATA[1]; at that time we set expected_block >1 so we can use this fact (expected_block > 1) as our "flag". However, we should check for LATCHED early in the game, and we don't find out about valid DATA[1] until late in the loop above. Additions are in green; in most cases we will become LATCHED during the first run through the main while loop.

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
                only check port if expected_block > 1    // see below for the case when expected_block == 1
                send error packet
                continue
        wrong length:    // can't even check for DATA opcode if there aren't enough bytes!
                continue
        not DATA
                continue
        create DATA packet out of replyDG
        wrong blocknum:
                continue
        // now we have a good packet!
                write data
                if expected_block == 1, set destport = replyDG.getport()
                expected_block ++;
                send ACK to destport
                if (size < 512) {
                        dally();            // to be discussed
                        break;            // done
                }
}

Now on to the timeout issue. There are two different uses of the term here. Suppose the timeout period is, say, 3000 ms.

A SocketTimeoutException, meaning that the socket received nothing at all for 3000 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 3000 ms (though we may have received other things). Let's call this a "soft timeout".

It should be clear that a 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.

The only way to resolve this is to check for the elapsed time. The current time is always available in System.currentTimeMillis(); you will save that value each time you send a packet:
        send_time = System.currentTimeMillis();
Now, the first solution to the soft-timeout problem is to check someplace before any "continue" statements that the elapsed time has not been exceeded. That works, but it turns out that a better solution is to notice that if you check elapsed time for soft timeouts, you no longer really need to do anything for hard timeouts (except restart the loop). The hard-timeout interval becomes the clock granularity, in effect: if the soft-timeout interval is 3000 ms and the hard-timeout interval is 1000 ms, then in the worst case you wait until 3000+1000 = 4000 ms before actually noticing and responding to the soft timeout.

Once you have an elapsed-time check, it makes sense to shorten the hard-timeout interval to something very small; 1000 ms or even 250 ms. On 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 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
send_time = System.currentTimeMilllis();

while (true) {
        check elapsed time: if exceeded (ie if a soft timeout),
                resend whatever was sent most recently
                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
        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
                send_time = System.currentTimeMilllis();                
                if (size < 512) {
                        dally();            // to be discussed
                        break;            // done
                }
}

I recommend that you implement this in phases, as follows.

Implement the two steps above under "basic transfer". These are also discussed below. This will mean that the vanilla transfer completes.

For each data packet received, write the data to System.out. (This is now done for you.)

Add sanity checks, for (in order) host/port, packet size, opcode, and block number.

Complete the process of latching on to the new port by making sure that the first packet meets all the tests (sanity checks) for Data[1] before you save its port number. You should also make sure the client stops when a packet with less than 512 bytes of data is received.

Handle timeouts, by retransmitting the most recently sent packet when the elapsed time exceeds a certain amount (2 seconds?). One way to do this is to keep a DatagramPacket variable LastSent, which can either be reqDG or ackDG, and just resend LastSent. Note that the response to an InterruptedIOException, a "true" timeout, will simply be to continue the loop again.

Add support for an dallying and error packets. After the client has received the file, dallying means to wait something like two timeout intervals (or more) to see if the final data packet is retransmitted. If it is, it means that the final ACK was lost. The dally period gives the client an opportunity to resend the final ACK. Error packets are to be sent to any sender of an apparent data packet that comes from the wrong port.

You can test your program by contacting the server, ulam.cs.luc.edu, and requesting files. If you contact on port 4715, you get a response from a new port (the standard behavior). This doesn't go through NAT firewalls, so if you contact ulam2 on port 4716 you get a response from the same port. No matter what file you ask for, the file you get is always the same. However, by asking port 4715 for the following names, you get the indicated behavior:

Explanation / Answer

Where in the code, we have good packet, it should be your "latched" data.

This is for latched.

this is hard timeout.

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
        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 fix is to clarify when we become LATCHED. This occurs when we recognize a valid DATA[1]; at that time we set expected_block >1 so we can use this fact (expected_block > 1) as our "flag". However, we should check for LATCHED early in the game, and we don't find out about valid DATA[1] until late in the loop above. Additions are in green; in most cases we will become LATCHED during the first run through the main while loop.

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
                only check port if expected_block > 1    // see below for the case when expected_block == 1
                send error packet
                continue
        wrong length:    // can't even check for DATA opcode if there aren't enough bytes!
                continue
        not DATA
                continue
        create DATA packet out of replyDG
        wrong blocknum:
                continue
        // now we have a good packet!
                write data
                if expected_block == 1, set destport = replyDG.getport()
                expected_block ++;
                send ACK to destport
                if (size < 512) {
                        dally();            // to be discussed
                        break;            // done
                }
}

send REQ
send_time = System.currentTimeMilllis();

while (true) {
        check elapsed time: if exceeded (ie if a soft timeout),
                resend whatever was sent most recently
                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
        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
                send_time = System.currentTimeMilllis();                
                if (size < 512) {
                        dally();            // to be discussed
                        break;            // done
                }
}

This is for latched.

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote