-
Notifications
You must be signed in to change notification settings - Fork 1
/
sender.c
225 lines (203 loc) · 7.65 KB
/
sender.c
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>
#include <errno.h>
#include <math.h>
#include <netdb.h>
#include "data.h"
#include "timer.h"
#include "net.h"
#include "packet.h"
int main(int argc, char **argv)
{
// Verify and parse args
if (argc < 4) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s server_IP server_port chunk_size window_size\n", argv[0]);
exit(1);
}
char *serverip = argv[1];
long int x = strtol(argv[2], NULL, 10);
if (x < 0 || x > USHRT_MAX) {
fprintf(stderr, "[error]: port %ld is invalid\n", x);
exit(1);
}
char *server_port = argv[2];
long int c = strtol(argv[3], NULL, 10);
if (c < 1 || c > MAXBUFSIZE) {
fprintf(stderr, "[error]: chunk_size should be between 1 and MAXBUFSIZE\n");
exit(1);
}
int32_t chunk_size = (int32_t) c;
long int w = strtol(argv[4], NULL, 10);
int32_t window_size = (int32_t) w;
printf("server_IP = %s\n", serverip);
printf("server_port = %s\n", server_port);
printf("chunk_size = %d\n", chunk_size);
printf("window_size = %d\n", window_size);
// Get network information
struct sockaddr addr;
int sock;
if (get_addr_sock(&addr, &sock, serverip, server_port) == -1) {
fprintf(stderr, "[sender]: couldn't get socket or addrinfo\n");
exit(1);
}
// Set initial timeout
if (set_timeout(sock, TIMEOUT_SEC) == -1) {
exit(1);
}
// Variables controlling window size. bufptr is the pointer to the start
// of where to pull data from g_buffer
char *bufptr = g_buffer;
int32_t base = 0;
int32_t nextseqnum = 0;
int32_t num_packets = (int32_t) ceil((strlen(g_buffer) + 1) / chunk_size);
int32_t last_ack = 0;
int32_t retransmissions = 0;
int32_t timedout = false;
struct packet_t *sentpkts = malloc(window_size * sizeof(struct packet_t));
// Send out window_size packets initially
for (int i = 0; i < window_size; ++i) {
// Make a new packet
int init_pktlen = chunk_size;
struct packet_t pkt;
int ret = make_packet(&pkt, 1, nextseqnum, init_pktlen, bufptr);
if (ret == -1) {
fprintf(stderr, "[sender]: couldn't make packet %d\n", nextseqnum);
goto cleanup_and_exit;
}
// If succesfully sent, cache the packet in the array of packets which
// represents our window.
if (send_packet(&pkt, sock, &addr) == -1) {
fprintf(stderr, "[sender]: couldn't send packet %d\n", nextseqnum);
goto cleanup_and_exit;
}
printf("SEND PACKET %d\n", pkt.seq_no);
sentpkts[nextseqnum % window_size] = pkt;
nextseqnum++;
bufptr += chunk_size;
}
// Main loop for sender activity
while (*bufptr != '\0') {
// If there was a timeout, resend the packets from base to nextseqnum - 1
if (timedout) {
for (int i = base; i < nextseqnum; ++i) {
struct packet_t oldpkt = sentpkts[i % window_size];
if (send_packet(&oldpkt, sock, &addr) == -1) {
fprintf(stderr, "[sender]: failed to resend packet %d\n", oldpkt.seq_no);
break;
}
printf("SEND PACKET %d\n", oldpkt.seq_no);
}
// Reset state variables and record retransmissions
timedout = false;
retransmissions++;
if (retransmissions > 10) {
fprintf(stderr, "[failure]: retried 10 times, could not send packets\n");
goto cleanup_and_exit;
}
}
// Can send a new packet because there's room in the window. Make a new packet
// and send it.
else if (nextseqnum < base + window_size) {
retransmissions = 0;
// Size the packet. If this is the last packet, it could potentially be smaller
size_t pktlen = chunk_size;
if (strlen(bufptr) < chunk_size) {
pktlen = strlen(bufptr) + 1;
}
// Make a new packet
struct packet_t pkt;
int ret = make_packet(&pkt, 1, nextseqnum, pktlen, bufptr);
if (ret == -1) {
fprintf(stderr, "[sender]: couldn't make packet %d\n", nextseqnum);
break;
}
// If succesfully sent, cache the packet in the array of packets which
// represents our window.
if (send_packet(&pkt, sock, &addr) == -1) {
fprintf(stderr, "[sender]: couldn't send packet %d\n", nextseqnum);
break;
}
printf("SEND PACKET %d\n", pkt.seq_no);
sentpkts[nextseqnum % window_size] = pkt;
// Increment bufptr so that it points to the start of the next data to send
bufptr += pktlen;
// If our base is the same as nextseqnum, we need to arm the timer
if (base == nextseqnum) {
if (set_timeout(sock, TIMEOUT_SEC) == -1) {
fprintf(stderr, "[sender]: couldn't set timeout\n");
goto cleanup_and_exit;
}
}
nextseqnum++;
}
// Receive an ACK and check if we can stop the timer. Update base and
// nextseqnum accordingly
struct ack_t ack;
if (recv_ack(&ack, sock, &addr) != -1) {
base = ack.ack_no + 1;
last_ack = ack.ack_no;
// If we've reached the nextseqnum, there are no outstanding packets
// so disable timer
if (base == nextseqnum) {
if (disable_timeout(sock) == -1) {
goto cleanup_and_exit;
}
}
printf("--------RECEIVED ACK %d\n", ack.ack_no + 1);
} else {
if (errno == EAGAIN) {
timedout = true;
} else {
fprintf(stderr, "[sender]: couldn't receive ACK\n");
goto cleanup_and_exit;
}
}
}
// Need to wait to see if we got all ACKs
while (last_ack < num_packets) {
struct ack_t ack;
if (recv_ack(&ack, sock, &addr) == -1) {
fprintf(stderr, "[sender]: couldn't receive remaining ACKs\n");
goto cleanup_and_exit;
}
printf("--------RECEIVED ACK %d\n\n", ack.ack_no);
last_ack = ack.ack_no;
}
// After sending all packets and receiving all ACKs, construct tear-down
// message (type=8 and len=0)
struct packet_t tear_down_pkt;
printf("Sending tear-down packet\n");
if (make_packet(&tear_down_pkt, 4, 0, 0, NULL) == -1) {
fprintf(stderr, "[sender]: couldn't construct tear-down packet\n");
goto cleanup_and_exit;
}
// Set timeout on socket (just to make sure it's still set for the
// final transmissions)
if (set_timeout(sock, TIMEOUT_SEC) == -1) {
goto cleanup_and_exit;
}
// Retransmit this packet 10 times, unless it gets an ACK of type=8
for (int i = 0; i < 10; ++i) {
if (send_packet(&tear_down_pkt, sock, &addr) == -1) {
fprintf(stderr, "[sender]: couldn't send tear-down packet\n");
goto cleanup_and_exit;
}
printf("SEND TEAR-DOWN PACKET\n");
struct ack_t tear_down_ack;
if (recv_ack(&tear_down_ack, sock, &addr) == -1) {
fprintf(stderr, "[sender]: couldn't receive tear-down ack\n");
goto cleanup_and_exit;
}
printf("-------- RECEIVED TEAR-DOWN ACK\n");
if (tear_down_ack.type == 8) {
break;
}
}
cleanup_and_exit:
free(sentpkts);
exit(1);
}