Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 7: Practical Applications

In this chapter you apply what you know about C to three projects: writing a simple game, building a small web server, and creating system utilities. Building real programs gives you a better sense of how to use C effectively.

7.1 Writing a simple game: Guess the Number

The first project is a simple number guessing game. The program generates a random number, and the user has to guess it.

Python:

import random

def guess_the_number():
    number = random.randint(1, 100)
    guess = None

    while guess != number:
        guess = int(input("Guess the number between 1 and 100: "))
        if guess < number:
            print("Too low!")
        elif guess > number:
            print("Too high!")
        else:
            print("Congratulations! You guessed the number.")

guess_the_number()

C:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    int number, guess;

    // Initialize random number generator
    srand(time(0));
    number = rand() % 100 + 1;

    printf("Guess the number between 1 and 100: ");

    do {
        scanf("%d", &guess);
        if (guess < number) {
            printf("Too low! Try again: ");
        } else if (guess > number) {
            printf("Too high! Try again: ");
        } else {
            printf("Congratulations! You guessed the number.\n");
        }
    } while (guess != number);

    return 0;
}

Explanation:

  • srand(time(0)) initializes the random number generator with the current time.
  • The rand() function then generates a random number between 1 and 100.
  • A do-while loop keeps asking the user for guesses until they get the number right.

7.2 Building a small web server

Next, you’ll build a simple web server. To keep it short, it answers every request with the same fixed text response instead of serving files, and it uses only basic socket programming.

Python:

import socket

def start_server():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('localhost', 8080))
    server_socket.listen(1)
    print("Server is running on port 8080...")

    while True:
        client_socket, client_address = server_socket.accept()
        request = client_socket.recv(1024).decode()
        print("Request received:")
        print(request)

        response = 'HTTP/1.0 200 OK\n\nHello, World!'
        client_socket.sendall(response.encode())
        client_socket.close()

start_server()

C:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    socklen_t addrlen = sizeof(address);
    char buffer[1024] = {0};
    char *hello = "HTTP/1.0 200 OK\n\nHello, World!";

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Bind the socket to the port 8080
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Server is running on port 8080...\n");

    // Accept incoming connection
    while (1) {
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }

        ssize_t bytes_read = read(new_socket, buffer, sizeof(buffer) - 1);
        buffer[bytes_read > 0 ? bytes_read : 0] = '\0';
        printf("Request received:\n%s\n", buffer);
        write(new_socket, hello, strlen(hello));
        close(new_socket);
    }

    return 0;
}

Explanation:

  • The server listens for incoming HTTP requests on port 8080.
  • When a request arrives, it sends back a simple “Hello, World!” response.
  • It is built from basic socket programming functions such as socket(), bind(), listen(), accept(), and write().
  • These functions return -1 when they fail, so the checks test for a negative result. A return value of 0 from socket() is a valid file descriptor.
  • read() doesn’t add a terminating '\0', so the code adds one after the last byte read before printing the buffer as a string.

7.3 Creating system utilities: file copy utility

The last project is a utility that copies the contents of one file to another.

Python:

def copy_file(src, dst):
    with open(src, 'r') as f_src:
        with open(dst, 'w') as f_dst:
            f_dst.write(f_src.read())

copy_file('source.txt', 'destination.txt')

C:

#include <stdio.h>
#include <stdlib.h>

void copyFile(const char *src, const char *dst) {
    FILE *f_src = fopen(src, "r");
    if (f_src == NULL) {
        perror("Error opening source file");
        exit(EXIT_FAILURE);
    }

    FILE *f_dst = fopen(dst, "w");
    if (f_dst == NULL) {
        perror("Error opening destination file");
        fclose(f_src);
        exit(EXIT_FAILURE);
    }

    int ch;
    while ((ch = fgetc(f_src)) != EOF) {
        fputc(ch, f_dst);
    }

    fclose(f_src);
    fclose(f_dst);
}

int main() {
    copyFile("source.txt", "destination.txt");
    printf("File copied successfully.\n");
    return 0;
}

Explanation:

  • The program opens the source file for reading and the destination file for writing.
  • It reads the source file character by character and writes each character to the destination file.
  • ch is an int, not a char, because fgetc returns an int: either a character value or EOF, which is a separate negative value. Stored in a char, a real byte could compare equal to EOF and end the copy early, or, where char is unsigned, the loop would never see EOF at all.
  • The program checks whether each file opened successfully and handles the error if it didn’t.