1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161 | 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() {
}
}
|