<file_sep>CC=gcc
DIRS=build build/client build/server

all: print_vars client server

print_vars:
	@echo "Using C compiler: " $(CC)

 
client: src/client.c build  
	$(CC) -o build/client/$@ $<

server:	src/server.c build
	$(CC) -o build/server/$@ $< -lpthread

clean:
    $(shell rm -rf $(DIRS))

build:
	$(shell mkdir -p $(DIRS))


<file_sep>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

static void print_command()
{
    printf("\nEnter file name to be uploaded to server\n");
    printf("$>");
}

static void print_usage()
{
    printf("Client application must be started with following arguments\n");
    printf("./client <IP_OF_SERVER> <PORT>\n");
}

static int connect_to_server(char* ip, char* port, int socket_fd)
{
    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(ip);
    servaddr.sin_port = htons(atoi(port));
    for (int i = 1; i <= 10; ++i) {
        if (connect(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr))) {
            printf("Connection failed retrying, attempt %d!\n", i);
            sleep(2);
        }
        else
            return 0;
    }
    return -1;
}

int main(int argc, char* argv[])
{

    if (argc != 3) {
        print_usage();
        exit(1);
    }

    char* ip = argv[1];
    char* port = argv[2];

    FILE* file = NULL;

    char buffer[4096];
    while (1) {

        int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_fd == -1) {
            perror("Error: ");
            exit(1);
        }

        int rc = connect_to_server(ip, port, socket_fd);
        if (rc == -1) {
            printf("Connection to %s:%s failed terminating application!\n", ip, port);
            close(socket_fd);
            exit(1);
        }

        print_command();

        // Optimization name points to buffer + 1 address
        // From buffer perspective name will be stored buffer+1
        // This is used to reuse resources since buffer[0] will containt file name size
        char* name = buffer + 1;

        // Handle CTRL + D and CTRL+Z in safe manner
        if (scanf("%s", name) == EOF) {
            clearerr(stdin);
            continue;
        }

        buffer[0] = strlen(name);

        // Opening file using 'rb' option since we do not actually need to modify file content.
        file = fopen(name, "rb");
        if (file == NULL) {
            printf("Could not open file %s", buffer);
            close(socket_fd);
            continue;
        }

        printf("File: %s, Size of message: %d\n", buffer, (int)strlen(buffer));

        if (send(socket_fd, buffer, buffer[0] + 1, 0) < 0) {
            perror("Error: ");
            close(socket_fd);
            continue;
        }

        while (!feof(file)) {
            // Reuse same buffer since it's max Buffer size 4096 as we need it
            int n = fread(buffer, 1, 4096, file);
            if (send(socket_fd, buffer, n, 0) < 0) {
                perror("Error: ");
                close(socket_fd);
                continue;
            }
        }
        fclose(file);
        close(socket_fd);
    }

    return 0;
}
<file_sep># DSA
Homework for DSA

Requirements: 
  make
  gcc compiler (with PTHREAD support)
  
Build:
 - make all 
 - make client (Compile only client application)
 - make server (Compile only server application)
 - make clean (Clean build directory)
  
 Applications are installed inside build directory build/client/ build/server/ under name client and server respectively.
 
 Usage of client application
 
  - ./client ip port 
  - eg: ./client 127.0.0.1 1337  
 
 Usage of server application 
  - ./server port MODE(THREAD || PROC)
  - eg: ./server 1337 THREAD
 
 
 MODE: 
 Server application can run in two modes 
 - 1. THREAD: Each new client that connnects to application will be handled in different thread.
 - 2. PROC: Each new client that connects to application will be handled in new proccess.
 
<file_sep>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

enum operation_mode {
    THREAD = 0,
    PROC = 1,
};

struct thread_arguments {
    int client_socket_fd;
};

enum operation_mode OPERATION_MODE = PROC;

static void handler_internal(int client_fd)
{
    char buf[4096];
    // Read 1 byte that indicates size of file name.
    char file_name_size;
    int n = recv(client_fd, &file_name_size, 1, 0);

    // Always prepare yourself for worst
    if (n == 0 || n == -1) {
        close(client_fd);
        return;
    }

    // Read file_name_size bytes from socket to form a file name.
    n = recv(client_fd, buf, file_name_size, 0);

    // Always prepare yourself for worst
    if (n == 0 || n == -1) {
        close(client_fd);
        return;
    }

    printf("Creating file %s\n", buf);
    FILE* file = NULL;
    file = fopen(buf, "wb");

    if (file == NULL) {
        close(client_fd);
        perror("Error: ");
        return;
    }

    while ((n = recv(client_fd, buf, 4096, 0)) && n != 0) {
        fwrite(buf, sizeof(char), n, file);
    }

    fclose(file);
    close(client_fd);

    printf("Succes!\n");
}

void* thread_handler(void* thread_args)
{
    pthread_detach(pthread_self());
    int client_fd = ((struct thread_arguments*)thread_args)->client_socket_fd;
    free(thread_args);
    handler_internal(client_fd);
    return NULL;
}

static void handler(int client_socket_fd)
{
    if (OPERATION_MODE == THREAD) {
        pthread_t tid;
        struct thread_arguments* thread_args;
        if ((thread_args = (struct thread_arguments*)malloc(sizeof(struct thread_arguments))) == NULL) {
            perror("Error: ");
            exit(1);
        }
        thread_args->client_socket_fd = client_socket_fd;
        pthread_create(&tid, NULL, thread_handler, (void*)thread_args);
    }
    else if (OPERATION_MODE == PROC) {
        if (fork() == 0) {
            handler_internal(client_socket_fd);
            exit(0);
        }
        else {
            perror("Error: ");
        }
    }
}

static void print_usage()
{
    printf("Server application must be started with following arguments\n");
    printf("./server <PORT> <MODE: THREAD || PROC >\n");
    printf("Example ./server 1337 THREAD\n");
}

int main(int argc, char* argv[])
{

    if (argc != 3) {
        print_usage();
        exit(1);
    }

    char* port = argv[1];
    char* mode = argv[2];

    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd == -1) {
        perror("Error: ");
        exit(1);
    }

    if (strcmp(mode, "THREAD") == 0) {
        printf("Server will run in thread mode!\n");
        OPERATION_MODE = THREAD;
    }
    else {
        printf("Server will run in new proccess mode!\n");
        OPERATION_MODE = PROC;
    }

    struct sockaddr_in server_addr;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(port));

    printf("> Starting server...\n");

    if (bind(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Error: ");
        return 1;
    }

    if (listen(socket_fd, 10) < 0) {
        perror("Error: ");
        return 1;
    }

    while (1) {
        printf("\nWaiting for connection!\n");
        int client_socket_fd;
        struct sockaddr_in client_address;
        unsigned int client_lenght;
        client_lenght = sizeof(client_address);

        if ((client_socket_fd = accept(socket_fd, (struct sockaddr*)&client_address, &client_lenght))) {
            printf("Handling client %s\n", inet_ntoa(client_address.sin_addr));
            handler(client_socket_fd);
        }
    }
    close(socket_fd);
    return 0;
}