-
Notifications
You must be signed in to change notification settings - Fork 1
/
servertemp.c
288 lines (256 loc) · 8.01 KB
/
servertemp.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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/* By Kuan Xiang Wen and Josh Camarena, Feb 2018
CS118 Project 2
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h> // definitions of a number of data types used in socket.h and netinet/in.h
#include <sys/socket.h> // definitions of structures needed for sockets, e.g. sockaddr
#include <netinet/in.h> // constants and structures needed for internet domain addresses, e.g. sockaddr_in
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <poll.h>
#include <sys/time.h>
#include <signal.h> /* signal name macros, and the kill() prototype */
#define MAX_PACKET_LENGTH 1024 // 1024 bytes, inluding all headers
#define MAX_PAYLOAD_LENGTH 1016 // 1024 - 8
#define HEADER_LENGTH 8
#define MAX_SEQ_NUM 30720 // 30720 bytes, reset to 1 after it reaches 30Kbytes
#define WINDOW_SIZE_BYTES 5120 // bytes, default value
#define WINDOW_SIZE_PACKETS 5
#define RTO 500 // retransmission timeout value
#define ACK 0x08
#define FIN 0x04
#define FRAG 0x02
#define SYN 0x01
//Simple error handling function. Prints a message and exits when called.
void error(char *msg)
{
perror(msg);
exit(1);
}
int sockfd, portno;
struct sockaddr_in serv_addr, cli_addr;
socklen_t addrlen = sizeof(serv_addr);
socklen_t cli_addrlen = sizeof(cli_addr);
int global_seq = 20000;
int fd = -1;
char filename[256] = {0}; // 255 chars max filename size
struct Packet {
unsigned short seq_num;
unsigned short ack_num;
unsigned short length;
unsigned char flags; // ACK, FIN, FRAG, SYN
char payload[MAX_PAYLOAD_LENGTH];
};
struct WindowFrame {
struct Packet packet;
int sent;
int ack;
int timeout;
struct timeval timesent_tv;
};
struct WindowFrame window[5] = {0};
int get_packet(struct Packet* rcv_packet) {
int recvlen = recvfrom(sockfd, rcv_packet, MAX_PACKET_LENGTH, 0, (struct sockaddr*) &cli_addr, &cli_addrlen);
if(recvlen > 0){
if(rcv_packet->ack_num > 0) printf("Receiving packet %d\n", rcv_packet->ack_num);
return 1;
}
return 0;
}
//Add pointer to frame field if an ACK is expected. Else, input NULL.
unsigned short send_packet(struct WindowFrame* frame, char* input, unsigned short datalen, unsigned short seq, unsigned short acknum,
unsigned char ackflag, unsigned char finflag, unsigned char fragflag, unsigned char synflag){
struct Packet tr_packet = {
.seq_num = seq,
.ack_num = acknum,
.length = datalen,
.flags = ACK*ackflag | FIN*finflag | FRAG*fragflag | SYN*synflag
};
if(input != NULL) memcpy(tr_packet.payload, input, datalen);
else memset(tr_packet.payload, 0, MAX_PAYLOAD_LENGTH);
printf("Sending packet %d %d", seq, WINDOW_SIZE_BYTES);
if(synflag) printf(" SYN");
else if(finflag) printf(" FIN");
printf("\n");
if(sendto(sockfd, &tr_packet, datalen+sizeof(tr_packet), 0, (struct sockaddr *)&cli_addr,cli_addrlen) < 0)
error("ERROR in sendto");
if(frame != NULL){
frame->packet = tr_packet;
frame->sent = 0;
frame->ack = 0;
frame->timeout = 0;
gettimeofday(&frame->timesent_tv,NULL);
//frame->timesent_tv;
}
return datalen+sizeof(tr_packet);
}
void retransmit(struct WindowFrame* frame){
char buf[MAX_PACKET_LENGTH];
memset(buf, 0, MAX_PACKET_LENGTH);
memcpy(buf,(void*) &frame->packet, MAX_PACKET_LENGTH);
printf("Sending packet %d %d", frame->packet.seq_num, WINDOW_SIZE_BYTES);
if(SYN & frame->packet.flags) printf(" SYN");
else if(FIN & frame->packet.flags) printf(" FIN");
printf(" Retransmission\n");
if(sendto(sockfd,buf, frame->packet.length + sizeof(frame->packet), 0, (struct sockaddr *)&cli_addr,cli_addrlen) < 0)
error("ERROR in sendto");
}
void init_file_transfer(){
int i;
char buf[MAX_PAYLOAD_LENGTH];
for(i=0; i < 5; i=i+1) {
// reads next part of file and puts it in window
int r = read(fd,buf,MAX_PAYLOAD_LENGTH);
if (r == 0) {
break; //File is completely transmitted!
}
else{
send_packet(&window[i], buf, sizeof(buf), global_seq, 0, 0, 0, 1, 0);
global_seq = global_seq + MAX_PACKET_LENGTH;
}
// once window is full, break
}
}
void file_transfer(unsigned short acknum){
// process window here
// check if any packets can be saved in order and move the window up
// then fill window with next payloads from next_file_window_frame
int i;
for(i = 0; i < 4; i++){
if(acknum == window[i].packet.seq_num - MAX_PACKET_LENGTH){
window[i].ack = 1;
break;
}
}
if(i == 5) return;//Probably repeated ACK
char buf[MAX_PAYLOAD_LENGTH];
i = 0; //Frame we are checking
int j = -1; //This iterator searches for the next un-ACKed frame
while(i < 4) {
// 'remove' consecutive acked packets from the beginning of the window array
// shift other frames in array
// yes i know this would be 'better' implemented as a queue with pointers
// where elements don't have to be shifted in an array
if (window[i].ack) {
j = i + 1;//Start this iterator
while(j < 4){
if(!window[j].ack){
window[i] = window[j];
i++;
j++;
}
else j++;
}
if(j >= 4) break;
} else i++;
}
if(j != -1){//j is set => repopulate window, starting from i
while(i < 4){
int r = read(fd,buf,MAX_PAYLOAD_LENGTH);
if (r == 0) {
break; //File is completely transmitted!
}
else{
send_packet(&window[i], buf, sizeof(buf), global_seq, 0, 0, 0, 1, 0);
global_seq = global_seq + MAX_PACKET_LENGTH;
}
i++;
}
}
}
//Primary event loop
int stateflag;
struct Packet rcv_packet;
void respond(){
get_packet(&rcv_packet);
switch(stateflag){
case 0://Awaiting SYN
if (rcv_packet.flags & SYN) {
char syn_buf[MAX_PAYLOAD_LENGTH];
memset(syn_buf, 0, MAX_PAYLOAD_LENGTH);
strncpy(filename, rcv_packet.payload, 256);
int finish = 0;// if 404, set fin to 1
if ((fd = open(filename, O_RDONLY)) < 0) finish = 1;
else{
struct stat s; //Declare struct
if (fstat(fd,&s) < 0){ //Attempt to use fstat()
error("fstat() failed");
}
memcpy(syn_buf, (void*) &s.st_size, sizeof(s.st_size));
}
// send file size
global_seq = global_seq + send_packet(NULL, syn_buf, sizeof(syn_buf), global_seq, rcv_packet.seq_num, 1,finish,0,1);
if(finish == 0) stateflag = 1;
}
if (rcv_packet.flags & FIN) {
send_packet(NULL, NULL, 0, global_seq, rcv_packet.seq_num, 0,1,0,0);
close(sockfd);
exit(0);
}
break;
case 1://Awaiting client handshake ACK - ensures data is allocated
if (rcv_packet.flags & ACK) {
init_file_transfer();
stateflag = 2;
}
break;
case 2://File transfer
if (rcv_packet.flags & ACK) {
file_transfer(rcv_packet.ack_num);
} else if (rcv_packet.flags & FIN) {
send_packet(NULL, NULL, 0, global_seq, rcv_packet.seq_num, 0,1,0,0);
close(sockfd);
exit(0);
}
break;
case 3://Await client timeout FINACK in case
break;
default:
break;
}
// printf("%d %d %d\n", header.ack_num, header.length, header.flags);
// printf("received message: %d \n%s\n", strlen(payload), payload);
// char* buf = "Server Acknowledged";
// send_packet(NULL, buf, 2002, 366, 0, 1, 0, 1);
}
//Sets up socket connection and starts server (based entirely off sample code).
//Calls response() and closes socket and returns right after.
int main(int argc, char *argv[])
{//Socket connection as provided by sample code
if (argc < 2) {
error("ERROR, no port provided");
exit(1);
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0); // create socket
if (sockfd < 0)
error("ERROR opening socket");
// fill in address info
memset((char *) &serv_addr, 0, sizeof(serv_addr)); // reset memory
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(portno);
stateflag = 0;
if (bind(sockfd, (struct sockaddr *) &serv_addr, addrlen) < 0)
error("ERROR on binding");
struct pollfd fds[] = {
{sockfd, POLLIN}
};
int r = 0;
while(1){
// use poll to detect when data is ready to be read from socket
r = poll(fds,1,0);
if (r < 0) {
// poll error
error("error in poll\n");
}
else if (fds[0].revents & POLLIN) {
respond();
}
}
return 0;
}