aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Sangdahl <[email protected]>2024-10-02 12:58:44 -0600
committerJack Sangdahl <[email protected]>2024-10-02 12:58:44 -0600
commitbba288a79171b2f3cf7efb72e56c0c6fb5cf6116 (patch)
treef3c8f96894097394685dd228b57c8b25a2ae54b5
parentfa8c37bb3ecd3cc6caba9b6bf465c360301432ea (diff)
downloadecen5273-bba288a79171b2f3cf7efb72e56c0c6fb5cf6116.tar.gz
ecen5273-bba288a79171b2f3cf7efb72e56c0c6fb5cf6116.zip
Programming Assignment 1: File Transfer
Signed-off-by: Jack Sangdahl <[email protected]>
-rw-r--r--.gitignore1
-rw-r--r--pa1_file_transfer/.gitignore5
-rw-r--r--pa1_file_transfer/Makefile14
-rw-r--r--pa1_file_transfer/readme.md60
-rw-r--r--pa1_file_transfer/uftp_client.c157
-rw-r--r--pa1_file_transfer/uftp_server.c176
-rw-r--r--readme.md3
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