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;
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
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!
// 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
}
}