-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathGBNSender.java
More file actions
172 lines (144 loc) · 5.18 KB
/
Copy pathGBNSender.java
File metadata and controls
172 lines (144 loc) · 5.18 KB
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
162
163
164
165
166
167
168
169
170
171
172
import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Deque;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.ArrayDeque;
import java.util.concurrent.Semaphore;
public class GBNSender {
private static final int ACK_SIZE = 12;
private static final int BUFFER_SIZE = 500;
private static final int HEADER_SIZE = 12;
private static final int SEQNUM_MODULO = 256;
private static final Semaphore available = new Semaphore(10);
private FileInputStream fileStream;
private DatagramSocket socket;
private InetAddress channelAddress;
private int port;
private Deque<Packet> queue;
private static final Object queueLock = new Object();
private Timer timer;
private int timeout;
private static final Object timerLock = new Object();
private volatile int base;
private volatile int nextSeqNum;
private volatile boolean sendFinished;
GBNSender(String file, String hostname, int port, int timeout) throws Exception {
this.port = port;
this.timeout = timeout;
base = 0;
nextSeqNum = 0;
channelAddress = InetAddress.getByName(hostname);
queue = new ArrayDeque<>();
fileStream = new FileInputStream(file);
sendFinished = false;
}
class TimeoutTask extends TimerTask {
public void run() {
// when timeout, retransmit all packets in the window
synchronized (queueLock) {
Iterator<Packet> itr = queue.iterator();
while (itr.hasNext()) {
Util.sendData(itr.next(), channelAddress, port, socket);
}
}
// schedule a new timeout task
synchronized (timerLock) {
timer.schedule(new TimeoutTask(), timeout);
}
}
}
private void receivePackets() {
byte[] buffer = new byte[ACK_SIZE];
DatagramPacket receiveDatagram = new DatagramPacket(buffer, buffer.length);
Packet packet;
while (!sendFinished || !queue.isEmpty()) {
try {
// get ack number
socket.receive(receiveDatagram);
packet = Packet.getPacket(receiveDatagram.getData());
System.out.println(String.format("PKT RECV ACK %s %s", packet.getLength(), packet.getSeqNum()));
int ackNum = packet.getSeqNum();
// calculate the number of received packets
int rcvNum = ackNum - base + 1;
if (ackNum < base) {
rcvNum += SEQNUM_MODULO;
}
// remove the received packet in the window
if (rcvNum <= 10) {
synchronized (queueLock) {
for (int i = 0; i < rcvNum; i++) {
queue.poll();
available.release();
}
}
base = (ackNum + 1) % SEQNUM_MODULO;
}
// reset timer
if (base == nextSeqNum) {
timer.cancel();
} else {
startTimer();
}
} catch (Exception e) {
System.out.println("Exception when receiving datagram packet");
}
}
}
private void startTimer() {
synchronized (timerLock) {
timer.cancel();
timer = new Timer();
timer.schedule(new TimeoutTask(), timeout);
}
}
public void start() throws Exception {
// create a new timer
timer = new Timer();
// create socket to send and receive data
socket = new DatagramSocket();
// create new thread to receive ACK packets
Thread receiveThread = new Thread(new Runnable() {
@Override
public void run() {
receivePackets();
}
});
receiveThread.start();
// send file data
System.out.println("Start to send file data");
while (true) {
// make packet
byte[] buffer = new byte[BUFFER_SIZE];
int readNum = fileStream.read(buffer, 0, BUFFER_SIZE);
if (readNum < 0) {
sendFinished = true;
break;
}
Packet packet = new Packet(0, readNum + HEADER_SIZE, nextSeqNum, buffer);
// send the packet
available.acquire();
synchronized (queueLock) {
queue.offer(packet);
}
Util.sendData(packet, channelAddress, port, socket);
// reset timer
if (base == nextSeqNum) {
startTimer();
}
// update nextSeqNum
nextSeqNum = (nextSeqNum + 1) % SEQNUM_MODULO;
}
// join the receive thread
receiveThread.join();
// end sender session
Util.endSenderSession(base, channelAddress, port, socket);
System.out.println("Finish sending file");
socket.close();
fileStream.close();
timer.cancel();
}
}