Some further notes on implementing the BLAST client 1. When the VERY FIRST valid fragment arrives, you will at that time set the following variables. None may ever change subsequently. numfrags port (of sender) MID (Message ID) The sender IP address can be set at this point too, BUT it might be easier to set it when you first contact the server. You can assume Protocol_number is 0. It is best if you check fragment validity first, BUT I do not believe my server intentionally sends any invalid initial fragments, so you can defer worrying about this issue until Later. It is also convenient, for later completion-testing, to set a variable finalMask at this time too. Set it to blastpkt.maskmaker(numfrags). If numfrags == 6, for example, this will set finalMask to 00111111. As each future fragment arrives, you can test if you're done with if (fragmask == finalMask) ... 2. When you first send the request, set starttime and, logically, the first timer (LAST_FRAG). When this timer expires, you reset it to RETRY. On expiration of any timer, you either send an SRR and reset the timer to RETRY, or else it's time to give up. You'll need a counter to keep track of RETRYs. 3. The best way to check if a timer has expired is if (System.currentTimeMillis() - start_time >= current_timer) It is *not* necessary to take steps to respond as soon as the timer expires; if you're 100 ms late that is fine. This suggests the implementation possibility of simply using sockettimeouts as your chance to test the timer, as above, before going back to your main waiting-for-packets call. This means that an outline of your main loop might be: while (true) if (System.currentTimeMillis() - start_time >= current_timer) { deal-with-it(); } try { s.recv(...); } catch (SocketTimeoutException ste) { // preferred to InterruptedIOException! continue; } ^^^^^^^^ validate-packet-and-save/process-as-appropriate(); } 4. It is strongly recommended that you write a send_SSR() method. You may base it on the bclient.java inline code for creating the SSR for message 0. Parameters (or global fields needed) for send_SSR() will include the IP address and port, the DatagramSocket, the fragmask, the number of fragments, and the MID. The fragmask should most likely be a parameter, *not* a global field. Note that, in the blastpkt.srr() constructor, the numfrags field is short, not an int. You're likely to need a cast here. (The same is true of the type field, except blastpkt.TYPE_SSR is *declared* short.) In the inline-code example provided in bclient.java, for creating an SSR for mesgnum == 0, the numfrags field is listed as (short) 1 sendSRR() is called when you time out, and also when you have received all the fragments and are done. 5. A simple data structure for storing fragments is an array of blastpkt.data, of size 32 (the maximum number of fragments); eg blastpkt.data[] frags = new blastpkt.data[32]; or ArrayList frags = new ArrayList(32); HOWEVER, there's a problem here: you need to store COPIES of the buffers (or clones), or else make sure that each time you read in a packet in the main loop you use a newly constructed buffer. Otherwise you're reusing the same buffer each time, and each entry will overwrite the previous one. It's easier to copy if you just store the data: byte[] [] frags = new byte[32] [] // messy array of arrays Now, frags[i] is the byte[] array representing the ith fragment. You would set frags[fragpos] = bdata.Data(), or, if you wanted a copy (which you do), byte[] buf = new byte[bdata.Data().length]; for (int i = 0; i< buf.length; i++) buf[i] = bdata.Data()[i]; frags[fragpos] = buf; When you've received all the fragments (if fragmask == finalmask), you can at that time go through this array/ArrayList in order and process (either with MD5update or with System.out) the Data() fields of each fragment. 6. Sanity checks: if you create a method for them, then you'll either have to make the "expected" values, for such things as numfrags, port, and MID, be parameters, or else you'll have to make the expected values global. If you do the tests inline, it is a little cleaner if you write them as "negative" tests: if (port-is-bad) { error ("bad port ..."); continue; } if (length-is-bad) { error ("bad length ..."); continue; } if (MID-is-bad) { error ("Bad MID ..."); continue; } This approach avoids the deep nesting of positive tests: if (port-is good) { if (length-is-good) { if (MID-is-good) { ... else // bad MID else // bad length else // bad port The use of continue statements also means that if any test fails, you've gone back to the top of the loop, so that if you ever reach the end of the tests then the packet must be good. 7. MD5 checksums: message 1: b419136552fd9f88453ebcd12c570595 message 2: 4b7de422b083817079dc079fe0ba1cfc message 3: 2627bd323ae6705c44afdb609c9ca3b9