diff options
author | Jack Sangdahl <[email protected]> | 2024-10-02 12:58:44 -0600 |
---|---|---|
committer | Jack Sangdahl <[email protected]> | 2024-10-02 12:58:44 -0600 |
commit | bba288a79171b2f3cf7efb72e56c0c6fb5cf6116 (patch) | |
tree | f3c8f96894097394685dd228b57c8b25a2ae54b5 | |
parent | fa8c37bb3ecd3cc6caba9b6bf465c360301432ea (diff) | |
download | ecen5273-bba288a79171b2f3cf7efb72e56c0c6fb5cf6116.tar.gz ecen5273-bba288a79171b2f3cf7efb72e56c0c6fb5cf6116.zip |
Programming Assignment 1: File Transfer
Signed-off-by: Jack Sangdahl <[email protected]>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | pa1_file_transfer/.gitignore | 5 | ||||
-rw-r--r-- | pa1_file_transfer/Makefile | 14 | ||||
-rw-r--r-- | pa1_file_transfer/readme.md | 60 | ||||
-rw-r--r-- | pa1_file_transfer/uftp_client.c | 157 | ||||
-rw-r--r-- | pa1_file_transfer/uftp_server.c | 176 | ||||
-rw-r--r-- | readme.md | 3 |
7 files changed, 416 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..211d056 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +textbook.pdf diff --git a/pa1_file_transfer/.gitignore b/pa1_file_transfer/.gitignore new file mode 100644 index 0000000..598ee26 --- /dev/null +++ b/pa1_file_transfer/.gitignore @@ -0,0 +1,5 @@ +client +server +*.o +*.txt +*.out diff --git a/pa1_file_transfer/Makefile b/pa1_file_transfer/Makefile new file mode 100644 index 0000000..77b46f7 --- /dev/null +++ b/pa1_file_transfer/Makefile @@ -0,0 +1,14 @@ +CFLAGS = -Wall -g + +all: server client + +server: uftp_server.o + cc $(CFLAGS) -o server uftp_server.o + +client: uftp_client.o + cc $(CFLAGS) -o client uftp_client.o + +clean: + rm *.o + rm server + rm client diff --git a/pa1_file_transfer/readme.md b/pa1_file_transfer/readme.md new file mode 100644 index 0000000..3ef2523 --- /dev/null +++ b/pa1_file_transfer/readme.md @@ -0,0 +1,60 @@ +## File Transfer +### Programming Assignment 1 - UDP Sockets + +In this assignment, you will build two programs in C: +- a client which will accept user input and then send requests over UDP +- a server which will return responses to those commands, also over UDP + +The client and server will implement functionality similar to FTP, which provides means for a +client to transfer files to and from a remote server. In our case, we will implement a subset of +some typical FTP commands and use sockets to communicate between the client and server. + +### Client +On invocation, the client must take two command line arguments +1. an IP address +2. a port number where the server side code is running + +```shell +$ cc uftp_client.c -o client +$ ./client 192.168.1.101 5001 +``` + +Your client should prompt the user to type any of the following commands: +- get [filename] +- put [filename] +- delete [filename] +- ls +- exit + +After verifying the syntax of the user's input is correct, your client will send a datagram to +the server indicating which operation is required any any other data or parameters needed to +satisfy the request. the client must then waitfor the server's response. if the server fails to +respond after an appropriate amount of time, an error message must be emitted to stderr. + +Once the server respons, your client will print an appropriate message to stdout, indicating the +result of the requested operation. In the case of `put`, the client should source the indicated +file in the current working directory. In the case of `get`, the client should store the returned +file in the current working directory. For the `exit` command, the client should exit gracefully +after the server acknowledges that the client is done. + +### Server +The server must take one command line argument +1. a port number for the server to use. You should choose ports greater than 5000 + +```shell +$ cc uftp_server.c -o server +$ ./server 5001 +``` + +Your server should wait for a UDP connection after binding to the requested port. Depending on +the commands received, the server responds to the client's request in the following manner: +- get [filename]: the server transmits the requested file to the client +- put [filename]: the server receives the transmitted file from the client and stores it locally +- delete [filename]: the server deletes the file if it exists +- ls: the server should search all the files it has in its current working directory and sent a +list of all these files to the client +- exit: the server should acknowledge that the client is done with a goodbye message. + +For any other command, the server should respond that the given command was not understood. In +all of these cases, if the server is unable to perform an operation, it should respond to the +client indicating a failure. diff --git a/pa1_file_transfer/uftp_client.c b/pa1_file_transfer/uftp_client.c new file mode 100644 index 0000000..6e1adb6 --- /dev/null +++ b/pa1_file_transfer/uftp_client.c @@ -0,0 +1,157 @@ +// File Transfer (Client) +// Jack Sangdahl +// 07 September 2024 +#include <fcntl.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#define DATA_SIZE 1024 + +int main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "usage: %s <ip_addr> <port_num>\n", argv[0]); + exit(EXIT_FAILURE); + } + char *hostname = argv[1]; + int port = atoi(argv[2]), sock; + if (port > 65535 || port < 5000) { + fprintf(stderr, "%s: port must be in range 5000-65535\n", argv[0]); + exit(EXIT_FAILURE); + } + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + fprintf(stderr, "%s: error opening socket\n", argv[0]); + exit(EXIT_FAILURE); + } + struct hostent *server = gethostbyname(hostname); + if (!server) { + fprintf(stderr, "%s: error determining server\n", argv[0]); + exit(EXIT_FAILURE); + } + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + server_addr.sin_addr.s_addr = INADDR_ANY; + memcpy((char *)&server_addr.sin_addr.s_addr, (char *)server->h_addr, + server->h_length); + socklen_t server_size = sizeof(server_addr); + char data[DATA_SIZE], command[DATA_SIZE], filename[DATA_SIZE]; + while (1) { + memset(data, 0, strlen(data)); + printf("usage: get [filename] | put [filename] | delete [filename] | ls | " + "exit\n"); + fgets(data, DATA_SIZE, stdin); + data[strcspn(data, "\n")] = 0; + sscanf(data, "%s %s", command, filename); + if (!strncmp(command, "get", 3)) { + if (sendto(sock, data, strlen(data), 0, (struct sockaddr *)&server_addr, + server_size) < 0) + fprintf(stderr, "%s: error sending data to server\n", argv[0]); + int rec_data; + if (recvfrom(sock, &rec_data, sizeof(rec_data), 0, + (struct sockaddr *)&server_addr, &server_size) < 0) + fprintf(stderr, "%s: error receiving status from server\n", argv[0]); + if (rec_data == -1) { + fprintf(stderr, "error: file %s does not exist on server\n", filename); + continue; + } + if (recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)&server_addr, + &server_size) < 0) + fprintf(stderr, "%s: error receiving file size\n", argv[0]); + int file_size = atoi(data); + printf("receiving file %s/%d bytes from server...\n", filename, + file_size); + int f = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + int bytes_transferred = 0; + while (bytes_transferred < file_size) { + memset(data, 0, sizeof(data)); + ssize_t received = + recvfrom(sock, data, sizeof(data), 0, + (struct sockaddr *)&server_addr, &server_size); + if (received < 0) + fprintf(stderr, "%s: error receiving file data\n", argv[0]); + write(f, data, received); + bytes_transferred += received; + printf("received %d/%d bytes\n", bytes_transferred, file_size); + } + close(f); + printf("file transferred successfully.\n"); + } else if (!strncmp(command, "put", 3)) { + printf("sending file %s to server...\n", filename); + int f = open(filename, O_RDONLY); + if (f < 0) { + fprintf(stderr, "error: file %s does not exist in directory\n", + filename); + continue; + } + if (sendto(sock, data, strlen(data), 0, (struct sockaddr *)&server_addr, + server_size) < 0) + fprintf(stderr, "%s: error sending data to server\n", argv[0]); + off_t file_size = lseek(f, 0, SEEK_END); + lseek(f, 0, SEEK_SET); + char file_data[DATA_SIZE]; + sprintf(file_data, "%ld", (long)file_size); + if (sendto(sock, file_data, DATA_SIZE, 0, (struct sockaddr *)&server_addr, + server_size) < 0) + fprintf(stderr, "%s: error sending file size to server\n", argv[0]); + memset(data, 0, sizeof(data)); + ssize_t bytes_read; + while ((bytes_read = read(f, file_data, sizeof(file_data))) > 0) { + if (sendto(sock, file_data, bytes_read, 0, + (struct sockaddr *)&server_addr, server_size) < 0) { + fprintf(stderr, "%s: error sending data to server\n", argv[0]); + break; + } + memset(file_data, 0, sizeof(file_data)); + } + if (bytes_read < 0) + fprintf(stderr, "error reading file %s\n", filename); + else + printf("file %s transferred successfully\n", filename); + close(f); + + } else if (!strncmp(command, "delete", 6)) { + if (sendto(sock, data, strlen(data), 0, (struct sockaddr *)&server_addr, + server_size) < 0) + fprintf(stderr, "%s: error sending data to server\n", argv[0]); + int rec_data; + if (recvfrom(sock, &rec_data, sizeof(int), 0, + (struct sockaddr *)&server_addr, &server_size) < 0) + fprintf(stderr, "%s: error receiving data from server\n", argv[0]); + switch (rec_data) { + case 0: + printf("file %s deleted successfully\n", filename); + break; + case 1: + printf("file %s does not exist on server\n", filename); + break; + case 2: + printf("error deleting file %s on server\n", filename); + break; + } + } else if (!strncmp(command, "ls", 2)) { + if (sendto(sock, data, strlen(data), 0, (struct sockaddr *)&server_addr, + server_size) < 0) + fprintf(stderr, "%s: error sending data to server\n", argv[0]); + if (recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)&server_addr, + &server_size) < 0) + fprintf(stderr, "%s: error receiving data from server\n", argv[0]); + printf("%s\n", data); + } else if (!strncmp(command, "exit", 4)) { + if (sendto(sock, data, strlen(data), 0, (struct sockaddr *)&server_addr, + server_size) < 0) + fprintf(stderr, "%s: error sending data to server\n", argv[0]); + if (close(sock) < 0) + fprintf(stderr, "%s: error closing socket\n", argv[0]); + printf("%s: server connection closed\n", argv[0]); + break; + } else + printf("invalid command!\n"); + } + return EXIT_SUCCESS; +} diff --git a/pa1_file_transfer/uftp_server.c b/pa1_file_transfer/uftp_server.c new file mode 100644 index 0000000..e170b3f --- /dev/null +++ b/pa1_file_transfer/uftp_server.c @@ -0,0 +1,176 @@ +// File Transfer (Server) +// Jack Sangdahl +// 07 September 2024 +#include <arpa/inet.h> +#include <dirent.h> +#include <fcntl.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#define DATA_SIZE 1024 + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "usage: %s <port_num>\n", argv[0]); + exit(EXIT_FAILURE); + } + int port = atoi(argv[1]), sock; + if (port > 65535 || port < 5000) { + fprintf(stderr, "%s: port must be in range 5000-65535\n", argv[0]); + exit(EXIT_FAILURE); + } + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + fprintf(stderr, "%s: error opening socket\n", argv[0]); + exit(EXIT_FAILURE); + } + struct sockaddr_in server_addr, client_addr; + memset(&server_addr, 0, sizeof(server_addr)); + memset(&client_addr, 0, sizeof(client_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons((short)port); + if (bind(sock, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < + 0) { + fprintf(stderr, "%s: error binding socket to port\n", argv[0]); + exit(EXIT_FAILURE); + } + printf("ready to receive client connections\n"); + socklen_t client_size = sizeof(client_addr); + char data[DATA_SIZE], command[DATA_SIZE], filename[DATA_SIZE]; + while (1) { + memset(data, 0, strlen(data)); + ssize_t n = recvfrom(sock, data, DATA_SIZE, 0, + (struct sockaddr *)&client_addr, &client_size); + if (n < 0) + fprintf(stderr, "%s: error receiving datagram from client\n", argv[0]); + struct hostent *host_info = + gethostbyaddr((const char *)&client_addr.sin_addr.s_addr, + sizeof(client_addr.sin_addr.s_addr), AF_INET); + if (!host_info) + fprintf(stderr, "%s: error determining host info\n", argv[0]); + char *host_addr = inet_ntoa(client_addr.sin_addr); + printf("%s: received datagram from %s (%s)\n", argv[0], host_info->h_name, + host_addr); + printf("received %d/%d bytes: %s\n", (int)strlen(data), (int)n, data); + sscanf(data, "%s %s", command, filename); + if (!strncmp(command, "get", 3)) { + printf("sending file %s to client...\n", filename); + int send_data, f = open(filename, O_RDONLY); + if (f < 0) { + send_data = -1; + fprintf(stderr, "error: file %s does not exist in directory\n", + filename); + } else + send_data = 1; + if (sendto(sock, &send_data, sizeof(send_data), 0, + (struct sockaddr *)&client_addr, client_size) < 0) { + fprintf(stderr, "%s: error sending status to client\n", argv[0]); + if (f >= 0) + close(f); + continue; + } + if (f < 0) + continue; + off_t file_size = lseek(f, 0, SEEK_END); + if (file_size < 0) { + fprintf(stderr, "error: determine file size of file %s\n", filename); + close(f); + continue; + } + lseek(f, 0, SEEK_SET); + sprintf(data, "%ld", file_size); + if (sendto(sock, data, strlen(data), 0, (struct sockaddr *)&client_addr, + client_size) < 0) { + fprintf(stderr, "%s: error sending file size to client\n", argv[0]); + close(f); + continue; + } + memset(data, 0, sizeof(data)); + ssize_t bytes_read; + while ((bytes_read = read(f, data, sizeof(data)))) { + if (sendto(sock, data, bytes_read, 0, (struct sockaddr *)&client_addr, + client_size) < 0) { + fprintf(stderr, "%s: error sending data to client\n", argv[0]); + break; + } + memset(data, 0, sizeof(data)); + } + if (bytes_read < 0) + fprintf(stderr, "%s: error reading from file %s\n", argv[0], filename); + close(f); + } else if (!strncmp(command, "put", 3)) { + printf("receiving file %s from client...\n", filename); + if (recvfrom(sock, data, DATA_SIZE, 0, (struct sockaddr *)&client_addr, + &client_size) < 0) + fprintf(stderr, "%s: error receiving data from client\n", argv[0]); + int file_size = atoi(data), + f = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (f < 0) + fprintf(stderr, "%s: error opening file %s\n", argv[0], filename); + int bytes_transferred = 0; + ssize_t bytes_received; + while (bytes_transferred < file_size) { + memset(data, 0, sizeof(data)); + int transfer_size = (file_size - bytes_transferred < DATA_SIZE) + ? (file_size - bytes_transferred) + : DATA_SIZE; + bytes_received = + recvfrom(sock, data, transfer_size, 0, + (struct sockaddr *)&client_addr, &client_size); + if (bytes_received < 0) { + fprintf(stderr, "%s: error receiving data from client\n", argv[0]); + close(f); + } + if (write(f, data, bytes_received) != bytes_received) { + fprintf(stderr, "%s: error writing data to file %s\n", argv[0], + filename); + close(f); + } + bytes_transferred += bytes_received; + printf("received %d/%d bytes\n", bytes_transferred, file_size); + } + close(f); + printf("file %s transferred successfully\n", filename); + } else if (!strncmp(command, "delete", 6)) { + int send_data, f = open(filename, O_RDWR); + if (f < 0) { + send_data = 1; + if (sendto(sock, &send_data, sizeof(int), 0, + (struct sockaddr *)&client_addr, client_size) < 0) + fprintf(stderr, "%s: error sending data to client\n", argv[0]); + } + close(f); + if (remove(filename) < 0) { + send_data = 2; + fprintf(stderr, "%s: error removing file %s\n", argv[0], filename); + } else + send_data = 0; + if (sendto(sock, &send_data, sizeof(int), 0, + (struct sockaddr *)&client_addr, client_size) < 0) + fprintf(stderr, "%s: error sending data to client\n", argv[0]); + } else if (!strncmp(command, "ls", 2)) { + char send_data[DATA_SIZE] = {0}; + DIR *d = opendir("."); + int data_len = 0; + struct dirent *curr; + while ((curr = readdir(d))) { + int name_len = strlen(curr->d_name); + memcpy(send_data + data_len, curr->d_name, name_len); + data_len += name_len; + send_data[data_len] = '\n'; + data_len++; + } + closedir(d); + if (data_len) + if (sendto(sock, send_data, strlen(send_data), 0, + (struct sockaddr *)&client_addr, client_size) < 0) + fprintf(stderr, "%s: error sending data to client\n", argv[0]); + } else if (!strncmp(command, "exit", 4)) + printf("received exit signal from client\n"); + } + return EXIT_SUCCESS; +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..10c792b --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +## CSCI4723 Networks Systems (FA24) +### Labs +1. File Transfer |