package rn; import java.io.IOException; import java.nio.ByteBuffer; import java.util.zip.CRC32; import javax.net.DataLinkLayer; import javax.net.ReadFromLowerLayerTimeoutException; public class SimpleDataLinkLayer extends DataLinkLayer { private static final int MAX_FRAME_PAYLOAD = 10; // maximale Größe der Nutzdaten private static final int HEADER_LENGTH = 4 + 4; // Gesamtlänge des Headers private static final int CRC_LENGTH = 8; // Länge der Checksumme private static final int ACK_TIMEOUT = 1000; // Zeit bis ACK kommen muss private static final int MAX_TRIES = 10; // maximale Sendungswiederholungen private int sequence = 0; // Sequenznummer für aktuelles Paket private int resentcounter = 0; // Sendungswiederholungszähler für aktuelles Paket private int current_tries = 0; public void mainloop() throws IOException, MaximumRetriesReachedException { // Daten von darüberliegender Schicht lesen (blockiert!) byte from_upper[] = new byte[MAX_FRAME_PAYLOAD]; int len_to_send = readFromUpperLayer(from_upper, MAX_FRAME_PAYLOAD); /* * Einen ByteBuffer mit der Länge, die das entgültige Paket haben wird erzeugen. D.h. die * Länge von Header plus Footer plus dem Payload, den wir von der darüberliegenden * Schicht erhalten haben */ ByteBuffer send_buffer = ByteBuffer.allocate(HEADER_LENGTH + len_to_send + CRC_LENGTH); /* * Die Daten aus dem Byte Array in den ByteBuffer übertragen */ send_buffer.position(HEADER_LENGTH); // Buffer-Zeiger Payload-anfang send_buffer.put(from_upper, 0, len_to_send); // Bytes für den Payload in Buffer schreiben /* * Die Schleife sendet das Paket mit den Nutzdaten mindestens einmal. Nach dem senden wird * für ACK_TIMEOUT Millisekunden auf ein ACK Paket gewartet. Trifft das nicht ein, wird * das Senden wiederholt und dabei der Wiederholungszähler erhöht */ ByteBuffer ack_buffer = null; do { // Sequenznummer und Wiederholungszähler hinzufügen send_buffer.rewind(); send_buffer.putInt(this.sequence); send_buffer.putInt(this.resentcounter++); // Daten senden writeToLowerLayer(send_buffer.array(), send_buffer.capacity()); setReadFromLowerLayerTimeout(ACK_TIMEOUT); this.current_tries++; // auf ACK warten try { byte from_lower[] = new byte[MAX_FRAME_PAYLOAD]; int len_ack = readFromLowerLayer(from_lower, MAX_FRAME_PAYLOAD); ack_buffer = ByteBuffer.allocate(HEADER_LENGTH + len_ack + CRC_LENGTH); } catch (ReadFromLowerLayerTimeoutException e) { /* * Wird dieser Punkt erreicht hat readFromLowerLayer zu lange gewartet. Damit ist * ack_buffer in jedem Fall leer und die Schleife wird wiederholt. * Wir prüfen hier noch ob die maximale Anzahl der Sendungswiederholungen erreicht * wurde und werfen nötigenfalls eine Exception. */ if(this.current_tries >= MAX_TRIES) { throw new MaximumRetriesReachedException(); } } } while(ack_buffer == null); } /** * Erzeugt eine CRC-32 Prüfsumme für den gegebenen Puffer. Die Berechnung erfolgt dabei von * Position 0 bis limit. * @param buffer Der ByteBuffer für den die CRC gebildet werden soll * @param limit Die Anzahl der bytes (vom Anfang des Buffers) für die CRC gebildet werden soll * @return Die gebildete CRC Prüfsumme */ private long createCRC(ByteBuffer buffer, int limit) { // Daten aus Buffer holen byte[] data = new byte[limit]; buffer.position(0); buffer.get(data, 0, limit); // CRC berechnen und zurückgeben CRC32 crc = new CRC32(); crc.update(data); return crc.getValue(); } /** * Überprüft für den gegebenen Buffer die enhaltene Prüfsumme druch erneute bildung und * anschließenden Vergleich. Die Prüfsumme steht dabei in den letzten 8 byte. * @param buffer Der zu überprüfende Buffer * @return true, wenn die Prüfsumme stimmt, andernsfalls false */ private boolean checkChecksum(ByteBuffer buffer) { // Prüfsumme aus dem Buffer holen int begin_crc = buffer.capacity() - CRC_LENGTH; long buffer_checksum = buffer.getLong(begin_crc); // Prüfsumme erneut bilden und vergleichen if(createCRC(buffer, begin_crc) == buffer_checksum) { return true; } return false; } /** * Erzeugt für den gegebene Buffer eine Prüfsumme und schreibt sie in die letzten 8 byte. * @param buffer Der Buffer für den die Prüfsumme erzeugt wird */ private void createChecksum(ByteBuffer buffer) { int begin_crc = buffer.capacity() - CRC_LENGTH; long checksum = createCRC(buffer, begin_crc); buffer.putLong(buffer.capacity() - CRC_LENGTH, checksum); } /** * Testmethode zum überprüfen der korrekten CRC Bildung */ public void testChecksum() { byte[] data = {1,2,3,1,15,123,0,1}; ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + data.length + CRC_LENGTH); buffer.position(HEADER_LENGTH); // Buffer-Zeiger Payload-anfang buffer.put(data, 0, data.length); // Bytes für den Payload in Buffer schreiben createChecksum(buffer); boolean result = checkChecksum(buffer); System.out.println(result); } public void receive() { } public void send() { } }