-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathclient.c
More file actions
498 lines (415 loc) · 19.6 KB
/
client.c
File metadata and controls
498 lines (415 loc) · 19.6 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
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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <string.h>
#include <math.h>
#include <conio.h>
#define REQUEST_BUFFER_SIZE 112
#define BUFFER_SIZE 50000
#define IP_ADDRESS_SIZE 20
#define MAX_FILENAME_SIZE 100
void printError(void); // function to display error messages
void TootOwnHorn(void); // function to toot own horn
void print_progress(int filesize, int totalbytes); // function to display progress of transfer
int no_digs(int); // function to calculate the number of digits in an integer
int main() {
WSADATA wsaData; // structure to hold winsock data
FILE *fp; // file pointer for transfer file
int retVal, read, nRx, j, written, remaining, filesize; // counters & flags for loops
int totalbytes = 0, nullpos = -1, firstblock = 1, details = 0, stop = 0; // counters & flags for loops
char request[REQUEST_BUFFER_SIZE]; // array to hold request bytes
char reply[BUFFER_SIZE]; // array to hold received bytes
char data[BUFFER_SIZE]; // array to hold data from data extraction
char filename[MAX_FILENAME_SIZE]; // array to hold filename of transfer file
char serverIP[IP_ADDRESS_SIZE]; // array to hold IP address of server
char command = '\0'; // character to hold user command
char debug = '\0'; // character to hold debug choice
char indicator; // character to hold server indicator response
int serverPort; // integer to hold port used by server
int no_digs_filesize; // integer to hold number of digits in the filesize
// 0. Welcome screen & debug mode option
printf("*************************************************\n");
printf(" INTERNET FILE TRANSFER - CLIENT\n");
printf("*************************************************\n");
printf("\nDebug mode (d) or normal mode (n):");
debug = getchar();
fflush(stdin);
// Main loop to allow multiple interactions with server
while(command != 'q'){
// 1. Winsock set up
// Initialise winsock, version 2.2, giving pointer to data structure
retVal = WSAStartup(MAKEWORD(2,2), &wsaData);
// Check for error
if (retVal != 0) {
printf("*** WSAStartup failed: %d\n", retVal);
printError();
return 1;
}
else if (debug == 'd') printf("WSAStartup succeeded\n" );
// 2. Client socket set up
// Create a handle for a socket, to be used by the client
SOCKET clientSocket = INVALID_SOCKET; // handle called clientSocket
// Create the socket, and assign it to the handle
// AF_INET means IP version 4,
// SOCK_STREAM means socket works with streams of bytes,
// IPPROTO_TCP means TCP transport protocol.
clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSocket == INVALID_SOCKET) { // Check for error
printf("*** Failed to create socket\n");
printError();
}
else if (debug == 'd') printf("Socket created\n" );
// 3. Server details
// If user has not yet been asked for server details or if user wants
// to change the server details, ask user to input address & port no.
if(details == 0) {
// Ask user for IP address
printf("\nEnter IP address of server: ");
scanf("%20s", serverIP);
// Ask user for port number
printf("Enter port number: ");
scanf("%d", &serverPort);
// Reset details marker to avoid having to re-enter server details
details = 1;
}
// Build a structure to identify the service required
// This has to contain the IP address and port of the server
struct sockaddr_in service; // IP address and port structure
service.sin_family = AF_INET; // Specify IP version 4 family
// Function inet_addr() converts IP address string to 32-bit number
service.sin_addr.s_addr = inet_addr(serverIP); // set IP address
// Function htons() converts 16-bit integer to network format
service.sin_port = htons(serverPort); // Set port number
// 4. Server connection
// Try to connect to the server
printf("\nTrying to connect to %s on port %d...", serverIP, serverPort);
retVal = connect(clientSocket, (SOCKADDR *) &service, sizeof(service));
// Check for error
if(retVal != 0) {
printf("Error connecting\n");
printError();
return 2;
}
else printf("\nConnected to %s on port %d \n", serverIP, serverPort);
// 5. Command choice
// Ask user for command
fflush(stdin);
printf("\nEnter command (enter 'h' for help): ");
command = getchar();
fflush(stdin);
// 5.1 Help
if (command == 'h') {
printf("\nCHARACTER\tCOMMAND\n"
" 'd'\t Download file from server\n"
" 'u'\t Safe upload file to server (will not upload if file already exists)\n"
" 'f'\t Force upload file to server (will replace file if already exists)\n"
" 'l'\t List files on server\n"
" 'r'\t Remove file from server\n"
" 'q'\t Quit program\n"
" 'c'\t Change server\n"
);
printf("\nEnter command: ");
command = getchar();
fflush(stdin);
}
// 5.2 List files from server
if (command == 'l') {
// First element of request array is command, second is null
request[0] = command;
request[1] = '\0';
// Send request array making sure to also send the null character
retVal = send(clientSocket, request, strlen(request) + 1, 0);
// send() arguments: socket handle, array of bytes to send,
// number of bytes to send, and last argument of 0.
if(debug == 'd') printf("\rSent %d bytes", retVal);
// Check for error
if(retVal == SOCKET_ERROR) {
printf("*** Error sending\n");
printError();
}
// Loop to receive list of files
// Make sure stop flag is correctly set
stop = 0;
do {
// Try to receive some bytes
// recv() arguments: socket handle, array to hold rx bytes,
// maximum number of bytes to receive, last argument 0.
nRx = recv(clientSocket, reply, REQUEST_BUFFER_SIZE, 0);
// nRx will be number of bytes received, or error indicator
// Check for error
if(nRx == SOCKET_ERROR) {
printf("Problem receiving\n");
stop = 1;
}
// Check if connection closed
else if (nRx == 0) {
printf("Connection closed by server\n");
stop = 1;
}
// Otherwise, we got some data
else if (nRx > 0) {
// Check if the received block is the final block
// Last element will be '\0' if it is final block
if(reply[nRx-1] == '\0') {
printf("%s", reply);
stop = 1; // If final block, exit receiving loop
}
// If not final block, print & keep receiving
else {
// Set element after final element to '\0' to safely print as string
reply[nRx] = '\0';
printf("%s", reply);
}
}
}while (!stop);
// Stop is reused later in the program so must be reset
stop = 0;
}
// 5.3 Remove file from server
else if(command == 'r') {
// Ask user for name of file to delete
printf("\nEnter filename of file to delete (example.ext): ");
scanf("%s", filename);
// First element of request array is transfer command
request[0] = command;
// Assign filename to the next available element of request array
sprintf(request + 1, "%s", filename);
// Send request array making sure to also send the null character
retVal = send(clientSocket, request, strlen(request) + 1, 0);
// send() arguments: socket handle, array of bytes to send,
// number of bytes to send, and last argument of 0.
if(debug == 'd') printf("Sent %d bytes", retVal);
// Check for error
if(retVal == SOCKET_ERROR) {
printf("*** Error sending\n");
printError();
}
}
// 5.4 Upload (safe or force)
else if(command == 'u' || command == 'f') {
// Ask user for name of file to upload
printf("\nEnter filename of file to upload (example.ext): ");
scanf("%s", filename);
// Open file for binary read & check for error
if((fp = fopen(filename, "rb")) == NULL) {
printf("\nError opening upload file: %s.\n", filename);
break;
}
if(debug == 'd') printf("\nSuccessfully opened %s\n", filename);
// Calculate file size
fseek(fp, 0, SEEK_END); // Set position to end of file
filesize = ftell(fp); // Current position is filesize in bytes
fseek(fp, 0, SEEK_SET); // Reset position to start of file
if(debug == 'd') printf("Calculated size of %s: %d bytes\n", filename, filesize);
// First element of request array is transfer command
request[0] = command;
// Convert filesize from integer to character array and assign
// string to request array (terminating in null character)
itoa(filesize, request + 1, 10);
// Calculate number of digits in filesize integer
no_digs_filesize = no_digs(filesize);
// Assign filename to the next avaiable element of request array
// Shifted by one command character, one null character and the size of the filesize
sprintf(request + (2 + no_digs_filesize), "%s", filename);
// Send request array making sure to also send the null character
// Size account for 2 null characters, one command character,
// the size of the filesize and the size of the filename.
retVal = send(clientSocket, request, (3 + no_digs_filesize + strlen(filename)), 0);
// send() arguments: socket handle, array of bytes to send,
// number of bytes to send, and last argument of 0.
// Check for error
if(retVal == SOCKET_ERROR) {
printf("*** Error sending\n");
printError();
}
else if(debug == 'd') printf("Sent %d bytes of request, waiting for reply from server...\n", retVal);
// Server response
// Try to receive a byte
// recv() arguments: socket handle, array to hold rx bytes,
// maximum number of bytes to receive, last argument 0.
nRx = recv(clientSocket, &indicator, 1, 0);
// nRx will be number of bytes received, or error indicator
if(debug == 'd' && nRx != SOCKET_ERROR) printf("Received %d bytes", nRx);
// Check for error
if(nRx == SOCKET_ERROR) {
printf("Problem receiving\n");
}
// Check if connection closed
else if (nRx == 0) {
printf("Connection closed by server\n");
}
// Otherwise we got the server indicator response
else if (nRx > 0) {
if(indicator == 'k' && debug == 'd') printf("\nServer has approved upload\n");
else if(indicator == 'f') printf("\n%s already exists on the server. Use force upload 'f' to overwrite\n\n", filename);
}
// File upload
// 'k' indicates server is approving upload
if(indicator == 'k') {
// Send data in blocks of size BUFFER_SIZE
remaining = filesize;
while(feof(fp) == 0 && ferror(fp) == 0) {
// Read data from file
read = (int)fread(data, 1, BUFFER_SIZE, fp);
// Check for error
if (ferror(fp)) {
perror("Send: Error reading input file");
fclose(fp); // Close input file
return 3; // We are giving up on this
}
// Debug
if(debug=='d') printf("Read %d bytes", read);
// Send extracted data & check for errors
retVal = send(clientSocket, data, read, 0);
if(retVal == SOCKET_ERROR) {
printf("*** Error sending response\n");
printError();
return 4;
}
// Update counters and print progress
remaining -= read;
// print_progress takes filesize and total bytes sent as args
print_progress(filesize, filesize - remaining);
}
// Toot the horn and close the file
TootOwnHorn();
fclose(fp);
}
}
// 5.5 Download
else if(command == 'd') {
// Ask user for name of file to download
printf("\nEnter filename of file to download (example.ext): ");
scanf("%s", filename);
// First element of request array is transfer command
request[0] = command;
// Assign filename to the next available element of request array
sprintf(request + 1, "%s", filename);
// Send request array making sure to send the null character
retVal = send(clientSocket, request, strlen(request) + 1, 0);
// send() arguments: socket handle, array of bytes to send,
// number of bytes to send, and last argument of 0.
if(debug == 'd') printf("Sent %d bytes of request, waiting for reply from server...\n", retVal);
// Check for error
if(retVal == SOCKET_ERROR) {
printf("*** Error sending\n");
printError();
}
// Loop to receive entire reply
totalbytes = 0;
firstblock = 1;
do {
// Try to receive some bytes
// recv() arguments: socket handle, array to hold rx bytes,
// maximum number of bytes to receive, last argument 0.
nRx = recv(clientSocket, reply, BUFFER_SIZE, 0);
// nRx will be number of bytes received, or error indicator
// If server sends only an x the file does not exist
if(reply[0] == 'x' && firstblock == 1) {
printf("File does not exist\n\n");
break;
}
// Check for error
if(nRx == SOCKET_ERROR) {
printf("Problem receiving\n");
stop = 1; // exit the loop if problem
}
// Check if connection closed
else if (nRx == 0) {
printf("Connection closed by server\n");
stop = 1;
}
// Otherwise we got some data
else if (nRx > 0) {
// Find end of file size marked by a null
// This will set j to zero on every run but will
// have no other effect after it has originally found the null character
for (j = 0; nullpos == -1; j++) if(reply[j] == '\0') nullpos = j;
if (firstblock == 1) {
// Open file and error check
if((fp = fopen(filename, "wb")) == NULL) printf("Error opening file for binary write.\n");
j = nullpos + 1; // Data starts after null ended file size
}
written = fwrite(reply + j, 1, nRx - j, fp); // Write to file
if(debug == 'd') printf("\t\tWrote %d bytes to file", written);
// Check for error
if (written != nRx - j) fprintf(stderr, "Writing error - count: %d written: %d\n", nRx - j, written);
// Not first block
if (firstblock != 1) totalbytes += nRx;
// First block
else {
filesize = atoi(reply);
firstblock = 0; // Disable flag
totalbytes += nRx - (nullpos + 1); // Subtract bytes for filesize
}
}
// Print progress indicator
print_progress(filesize, totalbytes);
// Continue until all file received, error or connection closed
} while ((totalbytes < filesize) && (stop == 0));
// Check exit condition
if(totalbytes >= filesize) {
TootOwnHorn();
fclose(fp);
}
}
// 5.6 Change server
else if(command == 'c') {
// Set details marker to allow re-entry of IP address and port details
details = 0;
}
// 6. Close connection
// Shut down the sending side of the TCP connection first
retVal = shutdown(clientSocket, SD_SEND);
// Check for error
if(retVal != 0) {
printf("*** Error shutting down sending\n");
printError();
}
// Then close the socket
retVal = closesocket(clientSocket);
// Check for error
if(retVal != 0) {
printf("*** Error closing socket\n");
printError();
}
else if(debug == 'd') printf("\nSocket closed\n");
}
// Finally clean up the winsock system
retVal = WSACleanup();
if(debug == 'd') printf("WSACleanup returned %d\n",retVal);
// Prompt for user input, so window stays open when run outside CodeBlocks
printf("\nPress return to exit:");
gets(request);
return 0;
}
// Function to calculate the number of digits in an integer
int no_digs(int number) {
return floor(1 + log10(number));
}
// Function to print informative error messages when something goes wrong
void printError(void) {
char lastError[1024];
int errCode;
errCode = WSAGetLastError(); // Get the error code for the last error
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
lastError,
1024,
NULL); // Convert error code to error message
printf("WSA Error Code %d = %s\n", errCode, lastError);
}
// Function to play sound when file transfer is complete
void TootOwnHorn(void){
Beep (553,300);Sleep(150);
Beep (661,300);Sleep(150);
Beep (883,300);Sleep(150);
}
void print_progress(int filesize, int totalbytes){
// typecasting could be avoided by reordering division and multiplication
// but fails at lower file sizes than this method
printf("\rLOADING: %3d%% ", (int)(((float)totalbytes/filesize)* 100));
}